C# 中的泛型允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,在声明类或方法的时候由于不知道用户要传入什么样类型的参数,所以在传入类型的地方“挖一个坑(“<T>”)",使用它的时候我们再用具体数据类型填上。
1、泛型类:
using System; 2.using System.Collections.Generic; 3.using System.Linq; 4.using System.Text; 5. 6.namespace Example 7.{ 8. class Data<T> //泛型类 9. { 10. public T[] n_data; //泛型变量 11. public Data(int size) //构造方法,new的时候调用构造方法开辟空间 12. { 13. n_data = new T[size]; 14. } 15. //输入 16. public void setdata(int index,T value) 17. { 18. n_data[index] = value; 19. } 20. //输出 21. public T getdata(int x) 22. { 23. return n_data[x]; 24. } 25. } 26. class Program 27. { 28. static void Main(string[] args) 29. { 30. Data<int> data = new Data<int>(5); 31. data.n_data[2] = 2; 32. Console.WriteLine(data.n_data[2]); 33. } 34. } 35.}
2、泛型方法:
using System; 2.using System.Collections.Generic; 3.using System.Linq; 4.using System.Text; 5. 6.namespace Example 7.{ 8. class Data<T> //泛型类 9. { 10. public T[] n_data; //泛型变量 11. public Data(int size) //构造方法,new的时候调用构造方法开辟空间 12. { 13. n_data = new T[size]; 14. } 15. //输入 16. public void setdata(int index,T value) 17. { 18. n_data[index] = value; 19. } 20. //输出 21. public T getdata(int x) 22. { 23. return n_data[x]; 24. } 25. } 26. class Program 27. { 28. static void Main(string[] args) 29. { 30. Data<int> data = new Data<int>(5); 31. data.n_data[2] = 2; 32. Console.WriteLine(data.n_data[2]); 33. } 34. } 35.}
泛型约束方法在form 窗口编程上的应用
private T OpenUniqueMDIChildWindow<T>(Form mdiParent) where T : Form, new() { foreach (Form subForm in mdiParent.MdiChildren) { if (!subForm.GetType().Equals(typeof(T))) { subForm.Close(); } else { subForm.Activate(); return subForm as T; } } T newForm = new T(); newForm.MdiParent = mdiParent; //newForm.FormBorderStyle = FormBorderStyle.None; //newForm.WindowState = FormWindowState.Maximized; //newForm.MaximizeBox = false; //newForm.MinimizeBox = false; newForm.StartPosition = FormStartPosition.CenterScreen; newForm.Show(); return newForm; } // private void btsmiBuildInfo_Click(object sender, EventArgs e) { OpenUniqueMDIChildWindow<BuildInfo>(this); } private void dtsmiDormInfo_Click(object sender, EventArgs e) { OpenUniqueMDIChildWindow<DormInfo>(this); }
3、泛型委托
using System; 2.using System.Collections.Generic; 3.using System.Linq; 4.using System.Text; 5. 6.namespace Example 7.{ 8. public delegate void MyDelegate<T>();//泛型委托 9. class Data<T> 10. { 11. private T a; 12. private T b; 13. public void setvalue(T x, T y) 14. { 15. a = x; 16. b = y; 17. } 18. //swap方法,ref是按地址传递 19. public void swap() 20. { 21. T temp; 22. temp = a; 23. a = b; 24. b = temp; 25. } 26. public void printvalue() 27. { 28. Console.WriteLine(a + "\t" + b); 29. 30. } 31. } 32. class program 33. { 34. static void Main(string[] args) 35. { 36. Data<string> data = new Data<string>(); 37. data.setvalue("HC","666"); 38. MyDelegate<string> my = new MyDelegate<string>(data.swap); 39. my += data.printvalue; 40. my(); //结果 666 HC 41. } 42. 43. } 44. 45. }
泛型类中数据类型的约束 程序员在编写泛型类时,总是会对通用数据类型T进行有意或无意地有假想,也就是说这个T一般来说是不能适应所有类型,但怎样限制调用者传入的数据类型呢?这就需要对传入的数据类型进行约束,约束的方式是指定T的祖先,即继承的接口或类。因为C#的单根继承性,所以约束可以有多个接口,但最多只能有一个类,并且类必须在接口之前。这时就用到了C#2.0的新增关键字:
public class Node<T, V> where T : Stack, IComparable
where V: Stack
{...}
以上的泛型类的约束表明,T必须是从Stack和IComparable继承,V必须是Stack或从Stack继承,否则将无法通过编译器的类型检查,编译失败。
通用类型T没有特指,但因为C#中所有的类都是从object继承来,所以他在类Node的编写中只能调用object类的方法,这给程序的编写造成了困难。比如你的类设计只需要支持两种数据类型int和string,并且在类中需要对T类型的变量比较大小,但这些却无法实现,因为object是没有比较大小的方法的。 了解决这个问题,只需对T进行IComparable约束,这时在类Node里就可以对T的实例执行CompareTo方法了。这个问题可以扩展到其他用户自定义的数据类型。
如果在类Node里需要对T重新进行实例化该怎么办呢?因为类Node中不知道类T到底有哪些构造函数。为了解决这个问题,需要用到new约束:
public class Node<T, V> where T : Stack, new()
where V: IComparable
需要注意的是,new约束只能是无参数的,所以也要求相应的类Stack必须有一个无参构造函数,否则编译失败。
C#中数据类型有两大类:引用类型和值类型。引用类型如所有的类,值类型一般是语言的最基本类型,如int, long, struct等,在泛型的约束中,我们也可以大范围地限制类型T必须是引用类型或必须是值类型,分别对应的关键字是class和struct:
public class Node<T, V> where T : class
where V: struct
泛型方法 泛型不仅能作用在类上,也可单独用在类的方法上,他可根据方法参数的类型自动适应各种参数,这样的方法叫泛型方法。看下面的类:
public class Stack2
{
public void Push<T>(Stack<T> s, params T[] p)
{
foreach (T t in p)
{
s.Push(t);
}
}
}
原来的类Stack一次只能Push一个数据,这个类Stack2扩展了Stack的功能(当然也可以直接写在Stack中),他可以一次把多个数据压入Stack中。其中Push是一个泛型方法,这个方法的调用示例如下:
Stack<int> x = new Stack<int>(100);
Stack2 x2 = new Stack2();
x2.Push(x, 1, 2, 3, 4, 6);
string s = "";
for (int i = 0; i < 5; i++)
{
s += x.Pop().ToString();
} //至此,s的值为64321