这篇文章主要来讲讲c#中的泛型,因为泛型在c#中有很重要的位置,对于写出高可读性,高性能的代码有着关键的作用。当我多次看到自己团队的代码中包含着大量的非泛型集合,隐式的装箱和拆箱操作时,我都会建议他们补一补泛型基础。
1,什么是泛型
2,为什么要使用泛型,泛型解决了什么问题
我们先来看看下面的代码(代码只是为了演示泛型,没有实际的意义),看看有什么问题?
1 class PRogram 2 { 3 static void Main(string[] args) 4 { 5 ArrayList array = new ArrayList(); 6 array.Add(1); 7 array.Add(2); 8 array.Add(3); 9 ArrayList resultArray = NumberSqrt(array);10 foreach (var item in resultArray)11 {12 Console.WriteLine(item);13 }14 }15 public static ArrayList NumberSqrt(ArrayList array)16 {17 ArrayList sqrtArray = new ArrayList();18 foreach (var item in array)19 {20 sqrtArray.Add(Math.Sqrt((double)(int)item));21 }22 return sqrtArray;23 24 }25 }
下面来看看泛型是如何解决这些问题的:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 List<double> array = new List<double>(); 6 array.Add(1); 7 array.Add(2); 8 array.Add(3); 9 List<double> resultArray = NumberSqrt(array);10 foreach (var item in resultArray)11 {12 Console.WriteLine(item);13 }14 }15 public static List<double> NumberSqrt(List<double> array)16 {17 List<double> sqrtArray = new List<double>();18 foreach (var item in array)19 {20 sqrtArray.Add(Math.Sqrt((double)(int)item));21 }22 return sqrtArray;23 24 }25 26 }
3,如何使用泛型:语法和规则
上面已经说了什么是泛型,以及为什么要用泛型,下面我们来聊聊如何使用泛型
1 public class Demo<T> { } 2 class Program 3 { 4 static void Main(string[] args) 5 { 6 object o = null; 7 Type t = typeof(Demo<>); 8 o = Activator.CreateInstance(t); 9 }10 }
如果为泛型类型的所有类型实参传递的都是实际数据类型,类型就称为封闭类型,CLR允许构造封闭类型的实例。
1 // 摘要: 2 // 支持在泛型集合上进行简单迭代。 3 // 4 // 类型参数: 5 // T: 6 // 要枚举的对象的类型。 7 public interface IEnumerator<out T> : IDisposable, IEnumerator 8 { 9 // 摘要:10 // 获取集合中位于枚举数当前位置的元素。11 //12 // 返回结果:13 // 集合中位于枚举数当前位置的元素。14 T Current { get; }15 }
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 MyClass<string> myclass = new MyClass<string>(); 6 myclass.ShowInfo("myClass"); 7 } 8 } 9 10 class MyClass<Ta>11 {12 public void ShowInfo(Ta a)13 {14 Type t = typeof(Ta);15 Console.WriteLine(t.FullName + "非泛型方法");16 }17 public void ShowInfo<Ta>(Ta a)18 {19 Type t = typeof(Ta);20 Console.WriteLine(t.FullName + "泛型方法");21 }22 public void ShowInfo<Tb>(Ta a, Tb b)23 {24 Type t = typeof(Tb);25 Console.WriteLine(t.FullName);26 }27 }
泛型方法的类型推断:c#语法中包含大量"<"和">"符号,所以导致代码的可读性和可维护性降低了,所以为了改变这种情况,c#编译器支持在调用一个方法时进行类型推断。例如下面的代码:myclass.ShowInfo("myClass", myclass);会调用ShowInfo<string>(string a, string b);
1 public static Boolean MethodTakingAnyType<T>(T o) {2 T temp = o;3 Console.WriteLine(o.ToString());4 Boolean b = temp.Equals(o);5 return b;6 }
看这个方法里面有一个临时变量temp。方法里面执行两次变量赋值和几次方法调用,无论T是值类型还是引用类型还是接口类型或者是委托类型这个方法都能工作。这个方法适用于当前存在的所以类型,也适用于将来可能定义的任何类型。比如你再看一下下面这个方法:
1 public static T MethodTakingAnyType<T>(T o1,T o2) {2 if (o1.CompareTo(o2) < 0)3 {4 return o1;5 }6 else {7 return o2;8 }9 }
在编译时会报如下错误(error CS0117:"T"不包含"CompareTo"的定义),因为并不是所有的类型都提供了CompareTo方法。那么在什么情况下T应该是什么类型呢?幸好,编译器和CLR支持一个称为约束的机制,可利用它使泛型变得真正有用!
1 public static Boolean MethodTakingAnyType<T>(T o) where T : struct2 {3 T temp = o;4 Console.WriteLine(o.ToString());5 Boolean b = temp.Equals(o);6 return b;7 }
c#的where关键字告诉编译器,为T指定的任何类型都必须是值类型。所以当你为T指定其它类型时,编译器会报错,例如你指定为string类型时(MethodTakingAnyType<string>("");)它会报错误 1 类型“string”必须是不可以为 null 值的类型才能用作泛型类型或方法
1 public static Boolean MethodTakingAnyType<T, TBase>(T o) where T : TBase2 {3 T temp = o;4 Console.WriteLine(o.ToString());5 Boolean b = temp.Equals(o);6 return b;7 }
T类型参数由TBase类型单数约束,也就是说不管T为什么类型,都必须兼容于TBase指定的类型实参。
1 public static Boolean MethodTakingAnyType<T, TBase>(T o) where T : new()2 {3 T temp = o;4 Console.WriteLine(o.ToString());5 Boolean b = temp.Equals(o);6 return b;7 }
1 public static void MethodTakingAnyType<T, TBase>(T o)2 {3 int x = (int)o;4 string s = (string)o;5 }
2,将一个泛型类型变量设置为默认值:o = default(T);这样的话,不管T为值类型还是引用类型都可以成功,如果T为引用类型时就设置为null,如果T为值类型时将默认值设为0;
3,将一个泛型类型变量与Null进行比较:使用==或者=!将一个泛型类型变量于null进行比较都是合法的。但是如果T为值类型时,o永远都不会为null查看下面的代码:1 public static void MethodTakingAnyType<T, TBase>(T o)2 {3 if (o == null) { 4 //do something5 }6 }
4,将两个泛型类型变量相互比较:如果T是值类型,下面的代码就是非法的。
1 public static void MethodTakingAnyType<T>(T o1,T o2)2 {3 if (o1 == o2) { 4 5 }6 }
4,泛型在使用过程的注意事项
新闻热点
疑难解答