C# 中级泛型-泛型比较与类型推断

泛型方法类型推断

有以下代码:

static List<T> MakeList<T>(T first,T second);
{
    ......
}

List<string> list = MakeList<string>("a1","b1");

可以简写成:

List<string> list = MakeList("a1","b1");

在这里就使用到了泛型类型推断,但是类型推断只适用于泛型方法。

默认值表达式

在一个泛型类当中,你如果要知道该类型参数的默认值,不能使用null,因为类型参数可能被约束为值类型,你也不能使用0,因为也有可能被约束为引用类型。在C#2当中提供了默认值表达式default(T),就可以返回类型实参的默认值。

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进行比较的时候才可以使用!===操作符。
如果类型参数被约束为值类型,那么就完全不能使用!===操作符。
如果他只是一个引用类型,则只能进行简单的引用比较,可以使用!===操作符。
如果他被进一步约束成继承自某个重载了的!===操作符的特定类型(即转换类型约束),就会使用重载操作符。

如果调用者指定的类型实参恰巧也进行了重载,那么这个重载操作符是不会使用的
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>只能比较它们本身和其他的值。

实现类似元组的泛型对值

    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);
        }
    }