C# 泛型中的數據類型判定與轉換

提到類型轉換,首先要明確C#中的數據類型,主要分為值類型和引用類型:

1.常用的值類型有:(struct)

整型家族:int,byte,char,short,long等等一系列

浮點家族:float,double,decimal

孤獨的枚舉:enum

孤獨的布爾:bool

2.常用的引用類型有:

string,class,array,delegate,interface

值得注意的是,無論是值類型還是引用類型,在C#中都派生于object,沒錯,這家伙就是萬惡之源!

正是因為有了這一特性,于是我們才能通過裝箱和拆箱愉快地將這些數據類型在值類型,object,引用類型間反復橫跳。

當然了,無論是裝箱和拆箱,對于性能都是有消耗的,不到萬不得已的時候盡量不要用(雖然我才不管這些,只要我用的爽就行了233)

 

雖然一般不提倡用object類型作為函數參數,取而代之使用泛型成為首選,那么如何判斷泛型參數的具體數據類型并進行有效轉換呢?

比如下面的例子:

 1 [System.Serializable]
 2 public struct Property<T> where T : struct
 3 {
 4     public string Label { get; }
 5     public T Value { get; }
 6     public PropertyType Type { get; }
 7     public Property(string label, T value, PropertyType type = PropertyType.Sub)
 8     {
 9         Label = label;
10         Value = value;
11         Type = type;
12     }
13 
14     public static Property<T> operator +(Property<T> a, Property<T> b)
15     {
16         var prop = new Property<T>();
17         if (a.Label == b.Label && a.Type == b.Type)
18         {
19             //怎么知道這個值到底是int還是float...
20         }
21         return prop;
22     }
23 }
1 public enum PropertyType
2 {
3     Main,
4     Sub
5 }

定義了一個名叫「屬性」的結構體,包含標簽,具體值和屬性類別(是主屬性還是副屬性),并使用泛型約束數據為值類型。

現在想要快速對這個結構體進行加法操作,于是增加操作符重載函數,方便愉快的對兩個屬性的值相加,但問題是泛型是無法強轉為任何一種非object數據類型,直接相加則更是不可能。

 

這時就想到了以object類型作為橋梁,進行具體的類型判定與轉換:

 1     public static Property<T> operator +(Property<T> a, Property<T> b)
 2     {
 3         if (a.Label == b.Label && a.Type == b.Type)
 4         {
 5             object tempa = a.Value;
 6             object tempb = b.Value;
 7 
 8             object add;
 9             if (tempa is int)
10             {
11                 add = (int)tempa + (int)tempb;
12             }
13             else if (tempa is float)
14             {
15                 add = (float)tempa + (float)tempb;
16             }
17             //...其他類型
18             else
19             {
20                 return new Property<T>();
21             }
22 
23             return new Property<T>(a.Label, (T)add, a.Type);
24         }
25         return new Property<T>();
26     }

判定類型時可以使用is關鍵字,也可直接取得值的類型或泛型類型進行判定:

1             if (tempa.GetType() == typeof(float))
2             {
3 
4             }
5             //or
6             if (typeof(T) == typeof(float))
7             {
8 
9             }

 

上面的方案雖然可以解決類型轉換的需求,但頻繁的拆箱和裝箱以及類型判定對性能的還是有一定影響,而且如果每一種類型都寫進if-else,看上去像千層塔一般難受。是時候輪到dynamic登場了。

.Net 4.0 以后開始支持動態數據類型——也就是dynamic關鍵字;令人興奮的是,dynamic可以被賦值為任何一種類型的值,當然也包括泛型。

然而值得注意的是,dynamic關鍵字并不會在程序編譯的時候進行校驗,而只在運行時動態判定,所以使用的時需要格外小心。

當然了,多次運行時的性能要遠遠高于裝箱和拆箱,而且書寫起來也是相當簡潔美觀(¯﹃¯):

 1     public static Property<T> operator +(Property<T> a, Property<T> b)
 2     {
 3         if (a.Label == b.Label && a.Type == b.Type)
 4         {
 5             dynamic x1 = a.Value;
 6             dynamic x2 = b.Value;
 7             return new Property<T>(a.Label, (T)(x1 + x2), a.Type);
 8         }
 9         return new Property<T>();
10     }

可以直接執行相加操作,但如果實際傳入的兩個數據類型并不能相加如bool,則會在運行時報錯;當然了,如果想進一步防止安全,還可以增加更多的類型判定語句,如:

 1     public static Property<T> operator +(Property<T> a, Property<T> b)
 2     {
 3         if (a.Label == b.Label && a.Type == b.Type)
 4         {
 5             if (typeof(T) != typeof(bool) && typeof(T)!=typeof(Enum))
 6             {
 7                 dynamic x1 = a.Value;
 8                 dynamic x2 = b.Value;
 9                 return new Property<T>(a.Label, (T)(x1 + x2), a.Type);
10             }
11         }
12         return new Property<T>();
13     }

 

補充一句,dynamic關鍵字在Unity中可能會報錯,因為Unity默認用的是.Net Api為2.0版本,需要升級為4.0之后的版本才能使用該關鍵字,具體設置如下:

 

 

下面做一個簡單測試:

 1 using UnityEngine;
 2 
 3 public class MicrosoftCSharpTest : MonoBehaviour
 4 {
 5     void Start()
 6     {
 7         dynamic a = 5.1f;
 8         dynamic b = 3;
 9         Debug.Log(a + b);
10 
11         var hp1 = new Property<int>("Hp", 41);
12         var hp2 = new Property<int>("Hp", 5);
13         var hp = hp1 + hp2;
14         Debug.Log(hp.Label + " : " + hp.Value);
15 
16         var miss1 = new Property<float>("MissRate", .1f);
17         var miss2 = new Property<float>("MissRate", .05f);
18         var miss = miss1 + miss2;
19         Debug.Log(miss.Label + " : " + miss.Value);
20     }
21 }

 

posted @ 2020-07-17 19:09  汐夜  閱讀(...)  評論(...編輯  收藏
最新chease0ldman老人