GC 관련해서 확인할 게 있어서 하다가 테스트하다가 보니,

Dctionary에 key로 enum을 사용하면 비교 과정에서 GC가 발생 하는 거로 알고 있었는데,

GC가 발생하지 않길래 뭐가 바뀌었는지 찾아봤다.


유니티 버전업 하면서 .net 3.5 -> .net 4.x 변경되면서 딕션어리 내부 로직도 변경됨.

.net 2.0 에서 .net 4.0로 업데이트될 때 Dictinaryt에 Key를 비교 시 사용되는  EqualityComparer<T> 생성 로직이 변경됐다.

변경 전에는 enum 을 key로 사용 시 IEqualityComparer<T>를 구현해서 넘겨주는 방식으로 해결되던 부분을 생략해도 되게 됨.

링크 참조.

https://referencesource.microsoft.com/#mscorlib/system/collections/generic/equalitycomparer.cs,40


변경된 내용 코드(CreateComparer 로직이 변경 됨.)

// .Net 2.0 ~ 3.5


private static EqualityComparer<T> CreateComparer() { Type typeFromHandle = typeof(T); if (typeFromHandle == typeof(byte)) { return (EqualityComparer<T>)(object)new ByteEqualityComparer(); } if (typeof(IEquatable<T>).IsAssignableFrom(typeFromHandle)) { return (EqualityComparer<T>)typeof(GenericEqualityComparer<int>).TypeHandle.CreateInstanceForAnotherGenericParameter(typeFromHandle); } if (typeFromHandle.IsGenericType && typeFromHandle.GetGenericTypeDefinition() == typeof(Nullable<>)) { Type type = typeFromHandle.GetGenericArguments()[0]; if (typeof(IEquatable<>).MakeGenericType(type).IsAssignableFrom(type)) { return (EqualityComparer<T>)typeof(NullableEqualityComparer<int>).TypeHandle.CreateInstanceForAnotherGenericParameter(type); } } return new ObjectEqualityComparer<T>(); }


// .Net 4.0 ~ 4.8

private static EqualityComparer<T> CreateComparer() { RuntimeType runtimeType = (RuntimeType)typeof(T); if (runtimeType == typeof(byte)) { return (EqualityComparer<T>)(object)new ByteEqualityComparer(); } if (typeof(IEquatable<T>).IsAssignableFrom(runtimeType)) { return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericEqualityComparer<int>), runtimeType); } if (runtimeType.IsGenericType && runtimeType.GetGenericTypeDefinition() == typeof(Nullable<>)) { RuntimeType runtimeType2 = (RuntimeType)runtimeType.GetGenericArguments()[0]; if (typeof(IEquatable<>).MakeGenericType(runtimeType2).IsAssignableFrom(runtimeType2)) { return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableEqualityComparer<int>), runtimeType2); } } if (runtimeType.IsEnum) { switch (Type.GetTypeCode(Enum.GetUnderlyingType(runtimeType))) { case TypeCode.Int16: return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ShortEnumEqualityComparer<short>), runtimeType); case TypeCode.SByte: return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(SByteEnumEqualityComparer<sbyte>), runtimeType); case TypeCode.Byte: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(EnumEqualityComparer<int>), runtimeType); case TypeCode.Int64: case TypeCode.UInt64: return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(LongEnumEqualityComparer<long>), runtimeType); } } return new ObjectEqualityComparer<T>(); }


유니티 샘플코드

using System.Collections.Generic;
using UnityEngine;

public class Test : MonoBehaviour {
    public enum TestA {
        A, B, C, D, F
    }
    public Dictionary<TestA, int> T = new Dictionary<TestA, int>();

    private void Awake()
    {
        T.Add(TestA.A, 1);
        T.Add(TestA.B, 2);
        T.Add(TestA.C, 3);
        T.Add(TestA.D, 4);
        T.Add(TestA.F, 5);
    }

    private void Update()
    {
        int v;
        T.TryGetValue(TestA.F, out v);
        T.ContainsKey(TestA.F);
    }
}

결과
.net 3.5


.net 4.x


반응형