# 泛型
# 泛型的定义
上一章讲的集合类,是没有类型化的,使用时需要把object项转换为实际的对象类型,因为所有的对象都继承自System.Object,所以任何对象都可以存储在ArrayList中,在操作时对一些类的操作可能并不适用于另一些类,导致崩溃,这时就想更好的办法是使用强类型化的集合类,这些类派生于CollectionBase,可以拥有自己的方法,来添加,删除和访问集合的成员,但它可能把集合成员限制为派生于某种类型的类,或必须支持某个接口,这样导致在定义集合中的新类时,必须符合下面的情况:
- 使用某个集合类,该类已经定义为可以包含新类型的项。
- 创建一个新的集合类,它包含新类型的项,实现所有需要的方法。
一般情况下,新的类型需要额外的功能,常常需要用到新的集合类,因此创建集合类会花费大量时间,在这样的情况下,泛型类便大大简化了这个问题,泛型类是以实例化过程中提供的类型为基础建立的,可以方便地对对象进行强类型化。泛型不只适合集合,但集合特别适用于泛型,它们大都在System.Collection.Generic名称空间,泛型并不限于类,还可以创建泛型接口,泛型方法,泛型委托。
注意在C++中,编译器可以检测出在哪里使用了模板的某个特定类型,在C#中所有的操作都在运行期间进行。它并不是被编译为许多类型,才能进行实例化,在.NET运行库允许在需要时动态生成泛型类,在实例化之前,不会存在该泛型类。
# 使用泛型
System.Collection.Generic名称空间中有两个泛型集合类型,它们是List<T>和Dictionary<K,V>,一个是T类型对象的集合,一个是KV键值对类型的集合。
# List
List<T>泛型集合类型更便于使用,不用像前面那样从CollectionBase派生一个类,然后实现需要的方法,被需要的方法已经实现了。
对泛型列表进行排序与对其他列表进行排序是一样的,前面讲了使用IComparer和IComparable接口比较两个对象,然后对该类型对象的集合进行排序,现在可以使用泛型接口IComparer<T>和IComparable<T>:
泛型方法 | 非泛型方法 |
---|---|
int IComparable<T>.CompareTo(T otherObj) | int IComparable.CompareTo(object otherObj) |
bool IComparable<T>.Equals(T otherObj) | 不存在,使用继承的other.Equals() |
int IComparer<T>.Compare(T objectA,T objectB) | int IComparer.Compare(object objectA,object objectB) |
bool IComparer<T>.Equals(T objectA,T objectB) | 不存在,使用继承的object.Equals() |
int IComparer<T>.GetHashCode(T objectA) | 不存在,使用继承的object.GetHashCode() |
除了上面的方法,还可以提供泛型委托,作为排序方法,有下面两个排序方法:
- Comparison<T>:这个委托用于排序方法,其返回类型和参数为:
int method(T objectA,T objectB)
- Predicate<T>:这个委托类型用于搜索方法,其返回类型和参数为:
bool method(T targetObject)
下面看实例:
class Book
{
public string bookName;
public int id;
public Book(string name,int idT)
{
bookName = name;
id = idT;
}
public override string ToString()
{
return "书名:" + bookName+" ;ID为:"+id;
}
}
class Library : List<Book>
{
public Library()
{
}
public Library(IEnumerable<Book> initItems)
{
foreach (var one in initItems) this.Add(one);
}
public void ShowBook()
{
foreach(var one in this)
{
Console.WriteLine(one);
}
}
}
class BookDelegate
{
public static int CompareBook(Book b1,Book b2)
{
if (b1.id != b2.id)
{
return b1.id - b2.id;
}
else
{
return b1.bookName.CompareTo(b2.bookName);
}
}
public static bool GetPartBook(Book book)
{
if(book.id < 5 && book.id >= 2)
{
return true;
}
else
{
return false;
}
}
}
class Program
{
static void Main(string[] args)
{
Library lib = new Library();
lib.Add(new Book("历史",3));
lib.Add(new Book("数学",1));
lib.Add(new Book("英语",3));
lib.Add(new Book("语文",4));
lib.Add(new Book("政治",5));
lib.Add(new Book("地理",2));
lib.Add(new Book("化学",2));
Comparison<Book> sorter = new Comparison<Book>(BookDelegate.CompareBook);
lib.Sort(sorter);
//lib.Sort(BookDelegate.CompareBook);
lib.ShowBook();
Console.WriteLine("-----------------");
Predicate<Book> searcher = new Predicate<Book>(BookDelegate.GetPartBook);
Library lib2 = new Library(lib.FindAll(searcher));
//Library lib2 = new Library(lib.FindAll(BookDelegate.GetPartBook));
lib2.ShowBook();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
注意上面的注释的部分会隐式创建Comparison<Book>和Predicate<Book>。
# Dictionary
Dictionary<K,V>类型可定义键/值对的集合,这个类需要实例化两个类型,分别用于键和值,以表示集合中的各项。
static void Main(string[] args)
{
Dictionary<string, int> student = new Dictionary<string, int>()
{
["liming1"] = 29,
["liming2"] = 25,
["zhangsan"] = 24,
["wangwu"] =26,
["zhenliu"] =28,
};
foreach(var one in student.Keys)
{
Console.WriteLine(one);
}
foreach(var one in student.Values)
{
Console.WriteLine(one);
}
Dictionary<string, int> stu2 = new Dictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
stu2.Add("Liming", 1);
stu2.Add("LiMing", 1);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 定义泛型
# 定义泛型类
上面介绍了使用泛型,现在我们让自定义类支持泛型。
class MyGClass<T1, T2, T3>
{
private T1 innerObj;
private T2 innerObj2;
public MyGClass(T1 item)
{
//innerObj = T1();
//不能假定T1是什么类型,有可能只是值类型,那就没有构造函数。
//只能假设T1继承自System.Object或可以封装到System.Object中。
innerObj = item;
innerObj2 = default(T2);
Console.WriteLine(innerObj2);
}
public T1 GetObj
{
get { return innerObj; }
}
public void GetALLType()
{
Console.WriteLine( "T1=" + typeof(T1).ToString() + ";T2=" + typeof(T2).ToString() + ";T3=" + typeof(T3).ToString());
}
public bool IfNull(T1 t1Obj,T2 t2Obj)
{
if (t1Obj == null)
{
Console.WriteLine("param1 is null!");
}
//if (t1Obj == t2Obj)
//不能比较两个泛型对象。
return false;
}
}
static void Main(string[] args)
{
MyGClass<int, string, char> MyObj = new MyGClass<int, string, char>(2);
MyObj.GetALLType();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
注意:
- default关键字可以根据泛型类型给变量设置默认值。
- 不能假定泛型参数是类类型或值类型,只能假定它是继承System.Object的类型。
# 泛型约束
上面的泛型被称为无绑定(unbounded)类型,没有对它们进行任何约束,通过约束(constraining)类型,可以限制用于实例化泛型类的类型,比如限制该泛型参数为继承自某个类型的类类型。
下面是一些可用约束:
约束 | 定义 |
---|---|
struct | 类型必须是值类型 |
class | 类型必须是引用类型 |
baseClassName | 类型必须是基类或继承自基类 |
interfaceName | 类型必须是接口或者实现了该接口 |
new() | 类型必须有一个公共的无参构造函数 |
来看实例:
class MyClass1
{
}
class MyClass2:MyClass1,MyInterace
{
}
class MyClass3 : MyClass2
{
}
interface MyInterace
{
}
class MyGClass<T1> where T1 : struct
{
}
class MyGClass2<T1> where T1 : class
{
}
class MyGClass3<T1> where T1:class,new()
{
}
class MyGClass4<T1,T2,T3> where T1:MyClass2 where T2 : T1 where T3:MyInterace
{
}
static void Main(string[] args)
{
MyGClass<int> MyGObj = new MyGClass<int>();
MyGClass2<MyClass1> MyGObj2 = new MyGClass2<MyClass1>();
MyGClass3<MyClass2> MyGObj3 = new MyGClass3<MyClass2>();
//MyGClass3<MyClass2> MyGObj3 = new MyGClass3<MyClass3>();
MyGClass4<MyClass3, MyClass3,MyClass3> MyGObj4 = new MyGClass4<MyClass3, MyClass3,MyClass3>();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
注意:
- 如果new()用作约束,它必须是为类型指定的最后一个约束。
- 可以把一个泛型参数作为另一个泛型参数的约束,这称为裸类型约束(naked type constraint),但是不能循环依赖。
- 约束必须在继承说明符的后面。
泛型约束在类继承中也是需要注意的,即便派生类的泛型参数是基类的泛型参数的超集也不行:
class MyClass1
{
}
class MyGClass2<T>where T:MyClass1
{
}
//class MyClass3 <T> :MyGClass2<T> where T:class
class MyGClass3 <T> :MyGClass2<T> where T:MyClass1
{
//打开的泛型类型
}
//关闭的泛型类型
class MyClass4 : MyGClass2<MyClass1>
{
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在类中可以使用隐式转换运算符简化一些操作,比如:
class Book
{
public string bookName;
public int id;
public Book(string name, int idT)
{
bookName = name;
id = idT;
}
public override string ToString()
{
return "书名:" + bookName + " ;ID为:" + id;
}
}
class Library<T>:List<T> where T : Book
{
public static implicit operator List<Book>(Library<T> lib)
{
List<Book> result = new List<Book>();
foreach(var one in lib)
{
result.Add(one);
}
return result;
}
public static Library<T> operator +(Library<T> lib1,List<T> list)
{
Library<T> lib = new Library<T>();
Console.WriteLine("-----------Use Library + List-----------");
foreach(var one in lib1){
lib.Add(one);
}
foreach(var one in list)
{
lib.Add(one);
}
return lib;
}
//public static Library<T> operator +(List<T> list, Library<T> lib) => list + lib;
//运行时错误哦
public static Library<T> operator +(List<T> list, Library<T> lib) => lib + list;
public void ShowBook()
{
foreach(var one in this) {
Console.WriteLine(one);
}
}
}
static void Main(string[] args)
{
Library<Book> lib = new Library<Book>();
lib.Add(new Book("历史",3));
lib.Add(new Book("数学",1));
lib.Add(new Book("英语",3));
List<Book> list = new List<Book>();
list.Add(new Book("语文",4));
list.Add(new Book("政治",5));
list.Add(new Book("地理",2));
list.Add(new Book("化学",2));
Library<Book> lib2 = lib + list;
lib2.ShowBook();
lib2 = list + lib;
lib2.ShowBook();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# 其他泛型类型
下面是定义泛型结构体:
//泛型结构体
struct MyStruct<T1, T2>
{
public T1 item1;
public T2 item2;
}
class MyClass1
{
}
class MyClass2:MyClass1
{
}
//泛型接口
interface MyInter1<T> where T : MyClass1
{
T GetClass();
}
class MyClass3 : MyInter1<MyClass2>
{
public MyClass2 GetClass()
{
throw new NotImplementedException();
}
}
//泛型方法
class MyClass4
{
public T GetDefault<T>() => default(T);
}
class MyClass5<T>
{
public T2 GetDefault<T2>() where T2:T => default(T2);
public T GetDefault<T,T2>() where T2:T => default(T);
}
//泛型委托
public delegate T1 MyDelegate<T1, T2>(T2 op1, T2 op2) where T1 : T2;
static void Main(string[] args)
{
MyStruct<int, string> myStruct = new MyStruct<int, string>();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 变体
变体(variance)是协变(covariance)和抗变(contravariance)的统称,这两个概念由 .Net 4引入,在之前虽然有它们的身影但是比较难实现,因为它们需要定制的编译过程。
泛型协变的类型参数可以把派生类泛型接口引用放在基类型变量中,协变类型参数只能用作方法的返回值或属性get访问器:
public interface Interface1<out T>
{
}
class MyClass1:Interface1<MyClass2>
{
}
class MyClass2 : MyClass1
{
}
static void Main(string[] args)
{
MyClass1 myObj1 = new MyClass1();
MyClass2 myObj2 = new MyClass2();
Interface1<MyClass2> myInterface2 = myObj2;
Interface1<MyClass1> myInterface1 = myInterface2;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
而泛型抗变类型则是把基类接口引用放在派生类型的变量中,抗变类型参数只能用作方法参数,不能用作返回类型:
public interface Interface1<in T>
{
}
class MyClass1:Interface1<MyClass1>
{
}
class MyClass2 : MyClass1
{
}
static void Main(string[] args)
{
MyClass1 myObj1 = new MyClass1();
MyClass2 myObj2 = new MyClass2();
Interface1<MyClass1> myInterface1 = myObj1;
Interface1<MyClass2> myInterface2 = myInterface1;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
比如List容器的Sort()方法可以使用它,我们自定义一个类,然后实现Compare方法。