提到类型转换,首先要明确C#中的数据类型,主要分为值类型和引用类型:安全
1.经常使用的值类型有:(struct)函数
整型家族:int,byte,char,short,long等等一系列性能
浮点家族:float,double,decimal测试
孤独的枚举:enumspa
孤独的布尔:boolcode
2.经常使用的引用类型有:blog
string,class,array,delegate,interfaceci
值得注意的是,不管是值类型仍是引用类型,在C#中都派生于object,没错,这家伙就是万恶之源!get
正是由于有了这一特性,因而咱们才能经过装箱和拆箱愉快地将这些数据类型在值类型,object,引用类型间反复横跳。string
固然了,不管是装箱和拆箱,对于性能都是有消耗的,不到万不得已的时候尽可能不要用(虽然我才无论这些,只要我用的爽就好了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 }