泛型方法类型推断
有以下代码:
1
2
3
4
5
6
|
static List<T> MakeList<T>(T first,T second);
{
......
}
List<string> list = MakeList<string>("a1","b1");
|
可以简写成:
1
|
List<string> list = MakeList("a1","b1");
|
在这里就使用到了泛型类型推断,但是类型推断只适用于泛型方法。
默认值表达式
在一个泛型类当中,你如果要知道该类型参数的默认值,不能使用null,因为类型参数可能被约束为值类型,你也不能使用0,因为也有可能被约束为引用类型。在C#2当中提供了默认值表达式default(T)
,就可以返回类型实参的默认值。
1
2
3
4
5
6
7
8
9
10
|
static int CompareToDefault<T>(T value) where T : IComparable<T>
{
return value.CompareTo(default(T));
}
Console.WriteLine(ComparaToDefault("x")); //1
Console.WriteLine(ComparaToDefault(10)); //1
Console.WriteLine(ComparaToDefault(0)); //0
Console.WriteLine(ComparaToDefault(-10)); //-1
Console.WriteLine(ComparaToDefault(DateTime.MinValue)); //0
|
由CompareTo文档指出,所有引用类型的值都大于null,所以返回1,CompareTo,如果比较的值比自己大则为1,相等则为0,小于则为-1。
不过如果传入的是null,以上代码会如预期的抛出NullReferenceException
异常,我们可以使用IComparer<T>
。
泛型比较
如果一个类型参数是未被约束的,那么只能在泛型类直线中对该类型的值与null进行比较的时候才可以使用!=
、==
操作符。
如果类型参数被约束为值类型,那么就完全不能使用!=
、==
操作符。
如果他只是一个引用类型,则只能进行简单的引用比较,可以使用!=
、==
操作符。
如果他被进一步约束成继承自某个重载了的!=
、==
操作符的特定类型(即转换类型约束),就会使用重载操作符。
如果调用者指定的类型实参恰巧也进行了重载,那么这个重载操作符是不会使用的
1
2
3
4
5
6
7
8
9
10
|
static bool AreReferenceEqual<T>(T first,T second) where T : class
{
return first == second;
}
string name = "Jon";
string intro1 = "Hello " + name;
string intro1 = "Hello " + name;
Console.WriteLine(intro1 == intro2);
Console.WriteLine(AreReferenceEqual(intro1,intro2));
|
在这里,第一行输出为True,是因为这里使用的是string重载过的比较操作符,用于比较两个字符串是否相等。
而第二行输出为False,是因为在编译AreReferenceEqual的时候,编译器根本不知道有哪些重载可用,因为我们约束的是class,所以相当于传入的类型实参是object。
不仅仅是操作符的时候,编译器会在编译未绑定泛型类型的时候就解析好所有方法重载,而不是等到执行时。
在对值进行比较的时,有两个相当有用的类,分别是EqualityComparer<T>
和Comparer<T>
,他们分别实现了IEqulalityComparer<T>
和IComparaer<T>
这两个接口。这两个类分别有一个Default的属性能够返回一个实现,能为特定的特定的类型才去正确的比较操作。
IComparaer<T>
和IComparable<T>
用于排序,判断某个值是小于、等于、还是大于另一个值。
IEqualityComparer<T>
和IEquatable<T>
则是通过某种标准来比较两个项的相等型,或者查找某个项的散列值。
IComparaer<T>
和IEqualityComparer<T>
的实例能够比较两个不同的值。
IComparable<T>和IEquatable<T>
只能比较它们本身和其他的值。
实现类似元组的泛型对值
1
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
|
public sealed class Pair<T1, T2> : IEquatable<Pair<T1, T2>> //这里我们实现了IEquatable,提供了一个强类型的API(Equals方法),可以避免不必要的运行时类型检查。
{
// 生成一个默认的比较器,可以自动处理null值的情况
private static readonly IEqualityComparer<T1> FirstComparer = EqualityComparer<T1>.Default;
private static readonly IEqualityComparer<T2> SecondComparer = EqualityComparer<T2>.Default;
private readonly T1 first;
private readonly T2 second;
public Pair(T1 first, T2 second)
{
this.first = first;
this.second = second;
}
public T1 First { get { return first; } }
public T2 Second { get { return second; } }
public bool Equals(Pair<T1, T2> other)
{
return other != null && FirstComparer.Equals(this.First, other.first) &&
SecondComparer.Equals(this.Second, other.Second);
}
public override bool Equals(object obj)
{
return Equals(obj as Pair<T1, T2>);
}
public override int GetHashCode()
{
return FirstComparer.GetHashCode(first) * 37 + SecondComparer.GetHashCode(second);
}
// public static Pair<T1,T2> Of<T1,T2>(T1 first,T2 second)
public static Pair<T1,T2> Of(T1 first,T2 second)
{
return new Pair<T1, T2>(first, second);
}
}
|