C#에서 enum을 boxing 없이 int로 변환하기
재미있는 글이 하나 있군요. ^^
C#에서 GC없이 enum을 int로 변환하기
; https://libsora.so/posts/csharp-enum-to-int/
글을 읽어보면 고민의 흔적이 와닿습니다. 게다가 저도 enum을 (int) 형 변환을 제외하고 boxing없이 저장할 수 없다는 것은 새롭게 인식이 되었습니다.
위의 문제를 정리해 보면, enum을 박싱 없이 다음과 같이 변환할 수 있습니다.
enum States {
Wait,
Run,
}
object [] list = new object[2];
object item = list[(int)States.Run];
하지만 저 int형 변환을 없애기 위해 다음과 같이 object[]와 함께 감싼 제네릭 클래스를 만들어도,
...
{
WrapperObject<States, object> states = new WrapperObject<States, object>(2);
states[States.Wait] = new object();
states[States.Run] = new object();
}
class WrapperObject<TEnum, TValue>
{
TValue[] data;
public WrapperObject(int count)
{
data = new TValue[count];
}
public TValue this[TEnum key]
{
get { return data[ConvertToIndex(key)]; }
set { data[ConvertToIndex(key)] = value; }
}
int ConvertToIndex(TEnum key)
{
var i = Convert.ChangeType(key, typeof(int));
return (int)i;
}
}
Convert.ChangeType의 반환값이 object이기 때문에 박싱이 발생해 버리는 것입니다. 그러면서
C# non-boxing conversion of generic enum to int? 글의 동적 메서드 생성 방법으로 해결을 해도 결국 iOS 버전의 제약으로 인한 IL2CPP의 DynamicMethod 사용 불가로 쓸 수 없게 되었다는 내용이 나옵니다.
처음 저 글을 읽었을 때는, 혹시 IConvertible을 이용하면 되지 않을까 싶었습니다. enum 타입이 암시적으로 상속받는 System.Enum 타입은 IConvertible을 상속받기 때문에,
[Serializable, ComVisible(true)]
public abstract class Enum : ValueType, IComparable, IFormattable, IConvertible
{
// ...[생략]...
}
Convert.ChangeType 대신 다음과 같이 쓰는 것도 가능합니다.
int i = (key as IConvertible).ToInt32(null);
하지만 여기서도 박싱이 발생하는 데 Enum.ToInt32 메서드를 보면 그 이유를 알 수 있습니다.
int IConvertible.ToInt32(IFormatProvider provider) =>
Convert.ToInt32(this.GetValue(), CultureInfo.CurrentCulture);
바로 저 GetValue() 메서드의 반환값이 object이기 때문입니다.
그렇습니다. enum은 암시적으로 int를 상속받은 값임에도 불구하고 명시적인 형 변환 연산자를 사용하지 않고서는 박싱없이 int로 값을 돌릴 방법이 없는 것입니다. 대신, 약간의 우회 방법을 쓰는 것을 생각해 볼 수 있습니다.
class WrapperObject<TEnum, TValue>
{
TValue[] data;
static Dictionary<TEnum, int> _enumKey = new Dictionary<TEnum, int>();
static WrapperObject()
{
int[] intValues = Enum.GetValues(typeof(TEnum)) as int[];
TEnum[] enumValues = Enum.GetValues(typeof(TEnum)) as TEnum[];
for (int i = 0; i < intValues.Length; i++)
{
_enumKey.Add(enumValues[i], intValues[i]);
}
}
public WrapperObject(int count)
{
data = new TValue[count];
}
public TValue this[TEnum key]
{
get { return data[_enumKey[key]]; }
set { data[_enumKey[key]] = value; }
}
}
보는 바와 같이, static 생성자에서 한 번만 처리하고 이후의 인스턴스에서는 재사용하기 때문에 성능 면에서 손해는 안 볼 것입니다.
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]