1. 程式人生 > >將C#裡的列舉Enum轉換為int

將C#裡的列舉Enum轉換為int

將列舉型別轉換為int在C#裡非常簡單,直接(int)強轉就行了,可是這樣寫程式碼不方便,尤其讓我這種懶癌患者難受,我喜歡用擴充套件方法的方式實現轉換(如果不理解擴充套件方法請自行補課,這裡不再介紹),比如這樣string轉int:

public static class ExtendString
{
	public static int ToInt(this string s, int defaultValue = 0)
	{
		int i;
		return int.TryParse(s, out i) ? i : defaultValue;
	}
}

比什麼Convert或者Parse不知道高到哪裡去了:

string str = "123";
int i = str.ToInt();

同樣處理Enum行不行?

public static class ExtendEnum
{
	public static int ToInt(this System.Enum e)
	{
		return (int)e;
	}
}

很可惜Enum型別的實參可以強轉為int,但是形參不行,編譯器提示無法轉換型別,非常令人無語。

不過用個object做中介就能強轉了:

public static int ToInt(this System.Enum e)
{
	return (int)(object)e;
}

這時我們的列舉變數都可以呼叫ToInt()了,貌似一切都成功了。

身為本文作者的我敏銳的發現這種做法有個致命缺陷——裝箱,它是程式碼效能驟降的一大元凶,我們要盡力避免它的存在。

先嚐試改動轉換方法:

public static int ToInt(this System.Enum e)
{
	return System.Convert.ToInt32(e);
}

結果發現e是以object型別被傳參的,跟前一種做法相比沒有本質區別。

思考良久我突然靈光一閃:任何物件都有 GetHashCode() 方法,用於返回該物件資料的Hash值,C#的常見內部型別都各自過載了該方法,比如int物件返回的Hash值就是它的數值本身,同為整型資料的Enum應該也返回它的數值才對。

為了防止搞出烏龍,那就查一下Enum類的原始碼吧,下面我把GetHashCode()的原始碼貼出來:

[System.Security.SecuritySafeCritical]
public override unsafe int GetHashCode()
{
	// Avoid boxing by inlining GetValue()
	// return GetValue().GetHashCode();

	fixed (void* pValue = &JitHelpers.GetPinningHelper(this).m_data)
	{
		switch (InternalGetCorElementType())
		{
			case CorElementType.I1:
				return (*(sbyte*)pValue).GetHashCode();
			case CorElementType.U1:
				return (*(byte*)pValue).GetHashCode();
			case CorElementType.Boolean:
				return (*(bool*)pValue).GetHashCode();
			case CorElementType.I2:
				return (*(short*)pValue).GetHashCode();
			case CorElementType.U2:
				return (*(ushort*)pValue).GetHashCode();
			case CorElementType.Char:
				return (*(char*)pValue).GetHashCode();
			case CorElementType.I4:
				return (*(int*)pValue).GetHashCode();
			case CorElementType.U4:
				return (*(uint*)pValue).GetHashCode();
			case CorElementType.R4:
				return (*(float*)pValue).GetHashCode();
			case CorElementType.I8:
				return (*(long*)pValue).GetHashCode();
			case CorElementType.U8:
				return (*(ulong*)pValue).GetHashCode();
			case CorElementType.R8:
				return (*(double*)pValue).GetHashCode();
			case CorElementType.I:
				return (*(IntPtr*)pValue).GetHashCode();
			case CorElementType.U:
				return (*(UIntPtr*)pValue).GetHashCode();
			default:
				Contract.Assert(false, "Invalid primitive type");
				return 0;
		}
	}
}

果然是強轉為int後再GetHashCode(),等價於強轉int

再看看最頂上的註釋,我跟微軟英雄所見略同啊哈哈

所以最後得到的程式碼為:

public static class ExtendEnum
{
	public static int ToInt(this System.Enum e)
	{
		return e.GetHashCode();
	}
}
結束