zoukankan      html  css  js  c++  java
  • c# in depth之泛型的实现

    c# in depth之泛型的实现

    1.默认值表达式

    如果已经明确了要处理的类型,也就知道了它的“默认”值。不知道要引用的类型,就不能直接指定默认值。不能使用null,因为它可能不是一个引用类型,不能使用0,因为它可能不是数值类型。虽然很少需要用到默认值,但它偶尔还是有用的。Dictionary<TKey,TValue>就是一个好的例子,它有个TryValue方法,它的作用有点儿像对数值类型进行处理的TryParse方法:他用一个输出参数来接收你打算获取的值,用一个Boolean返回值显示它是否成功。这意味着方法必须用TValue类型的值来填充输出参数。请记住,输出参数必须在方法正常返回之前赋值。

    为了满足这方面的要求,c#2提供了默认值表达式。虽然c#语言规范没有说他是一个操作符,但可以把它看做是与typeof相似的操作符,只是返回值不一样罢了。

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace 默认值表达式
    {
        class Program
        {
            static int CompareToDefault<T>(T value)
            where T : IComparable<T>
            {
                return value.CompareTo(default(T));
            }
    
            static void Main(string[] args)
            {
                Console.WriteLine(CompareToDefault("x"));
                Console.WriteLine(CompareToDefault(10));
                Console.WriteLine(CompareToDefault(0));
                Console.WriteLine(CompareToDefault(-10));
                Console.WriteLine(CompareToDefault(DateTime.MinValue));
            }
        }
    }
    复制代码

    运行结果:

    在上述代码中,我们为泛型方法使用了3种不同的类型:string,int和DateTime. CompareToDefault方法规定只能使用实现了IComparable<T>接口的类型作为参数,这样才能为传入的值调用ComparTo<T>.传入的值要和类型的默认值进行比较。string是引用类型,默认值是null—根据有关CompareTo的文档,所有引用类型的值都要大于null,所以第一个输出的结果是1,随后三行和int的默认值进行比较,显示int的默认值是0.最后一行输出0,显示了DateTime.MinValue就是DateTime的默认值。

    如果传递的参数是null,上述代码会抛出NullReferenceException异常。

    2.直接比较

    虽然上述代码演示了如何进行比较,但我们并不是总是愿意限制我们自己的类型来实现IComparable<T>或者它的姊妹接口IEquatable<T>,后者提供了一个强类型的Equals(T)方法,以弥补所有类型都具备的Equals(object)的不足。如果没有接口允许我们访问一些额外的信息,那么我们能做的事情就很少了。只能调用Equals(object)。如果要比较的值时值类型,它会造成装箱。

    如果一个类型是未约束的,就可以使用==和!=操作符,但只能将该类型的值与null进行比较。不能直接比较两个T类型的值(会报错,无法通过编译),如果类型实参是一个引用类型,会进行正常的引用比较。如果为T提供的类型实参是一个非可空值类型,与null进行比较总是不相等(这样一来,JIT编译器就可以移除这个比较)。如果类型实参是可空值类型,那么就会自然而然的与类型的空值进行比较。

    如果一个类型参数被约束成值类型,就完全不能使用==和!=。如果被约束成引用类型,那么具体执行的比较将完全取决于类型参数被约束成什么类型。如果它只是一个引用类型,那么执行的是简单的引用比较。如果被进一步约束成继承自某个重载了==和!=操作符的特定类型,就会使用重载运算符。但要注意,假如调用者指定的类型实参恰巧也进行了重载,那么这个重载操作符是不会使用的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    using System.Text;
    using System.Threading.Tasks;
     
    namespace 直接比较实现
    {
        class Program
        {
            static bool AreReferencesEqual<T>(T first, T second)
                where T:class
            {
                return first == second;
            }
            static void Main(string[] args)
            {
                string name = "Joy";
                string intro1 = "My name is "+name;
                string intro2 = "My name is "+name;
                Console.WriteLine(intro1==intro2);
                Console.WriteLine(AreReferencesEqual(intro1,intro2));
            }
        }
    }

     运行结果为:

      虽然string 重载了==,但在执行的比较中是不会用这个重载的。基本上,在说编译AreReferencesEqual<T>时,编译器根本不知道有哪些重载可用,就好比传入的只是object类型的参数。

      并非只有操作符才有这个问题,遇到泛型类型时,编译器会在编译未绑定时就解析好所有方法重载,而不是等到执行时,才去为每个可能的方法调用重新考虑是否存在更具体的重载。例如,Console.WriteLine(default(t));这个语句总是被解析成Console.WriteLine(object object),即使为T传递的类型恰好就是string,也不会调用Console.WriteLine(string value),这好比普通方法重载是发生在编译时,而不是执行时。

      需要对值进行比较时,有两个相当有用的类,他们是EqualityComparer<T>和Comparer<T>,两者都位于System.Collection.Generic命名空间中。他们分别实现了IEqualityComparer<T>(适合对字典进行比较和哈希处理)和IComparer<T>(适合排序)。这两个类的Default属性能返回一个实现,能为特点的类型采取正确的比较操作。

    说明:泛型比较接口  共有四个主要的泛型接口可用于比较。IComparer<T>和IComparable<T>用于排序(判断某个值是小于、等于还是大于另一个值),而IEqualityComparer<T>和IEquatable<T>通过某种标准来比较两个项的相等性,或查找某个项的散列(通过相等性方式匹配)

    如果换一种方式来划分这四个接口,IComparer<T>和IEqualiyComparer<T>的实例能比较两个不同的值,而IComparer<T>和IEquatable<T>的实例则可以比较它们本身和其他值。

    3.一个完整的比较实例,表示一对值

    这是一个完整的实例,它实现了一个有用的泛型类型,也就是一个Pair<T1,T2>,用于容纳两个值,类似键值对,但这两个值之间没有任何关系。

    除了提供属性来访问值本身之外,我们还覆盖了Equals和GetHashCode方法,从而使这个类型的实例能很好的作为字典中的键来使用。

     pair<T1,T2>类

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace 表示一对值的泛型类
    {
        public sealed class Pair<T1, T2> : IEquatable<Pair<T1, T2>>
        {
            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);
            }
        }
    }
    复制代码

    Pair<T1,T2>辅助类

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace 表示一对值的泛型类
    {
        public static class Pair
        {
            public static Pair<T1, T2> Of<T1, T2>(T1 first, T2 second)
            {
                return new Pair<T1, T2>(first,second);
            }
        }
    }
    复制代码

    主体方法

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    namespace 表示一对值的泛型类
    {
        class Program
        {
            static void Main(string[] args)
            {
                Pair<string, string> pair1 = new Pair<string, string>("hello","world");
                Pair<string, string> pair2 = new Pair<string, string>("hello", "world");
                Pair<int, string> pair3 = new Pair<int, string>(1,"hello world");
                bool c = pair1.Equals(pair2);
                bool d = pair2.Equals(pair3);
                System.Console.WriteLine(c);
                System.Console.WriteLine(d);
                System.Console.WriteLine(pair2.GetHashCode());
    
            }
        }
    }
    复制代码

    运行结果

     
     
    分类: c#语言
  • 相关阅读:
    周末之个人杂想(十三)
    PowerTip of the DaySorting Multiple Properties
    PowerTip of the DayCreate Remoting Solutions
    PowerTip of the DayAdd Help to Your Functions
    PowerTip of the DayAcessing Function Parameters by Type
    PowerTip of the DayReplace Text in Files
    PowerTip of the DayAdding Extra Information
    PowerTip of the DayPrinting Results
    Win7下IIS 7.5配置SSAS(2008)远程访问
    PowerTip of the DayOpening Current Folder in Explorer
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3433711.html
Copyright © 2011-2022 走看看