• 19.C# 泛型


    1.泛型的概念

    所谓泛型,即通过参数化类型来实现在同一份代码上操作多种数据类型。泛型编程是一种编程范式,它利用“参数化类型”将类型抽象化,从而实现更为灵活的复用。

    2. .net提供的泛型

    2.1可空类型System.Nullable<T>

    简单类型和引用类型的一个区别是简单类型必须包含一个值,引用类型可以是null,有时候需要把简单类型设置为可空类型,比如在处理数据库数据的时候。可以使用System.Nullable<T>声明一个可空类型,如System.Nullable<int> nullableInt = null;可以通过判断可空类型是否为null或者判断HasValue是否为true确定可空类型是否为空,如

                System.Nullable<int> nullableInt = null;
                if (nullableInt == null)
                { 
                    //
                }
                //等价于
                if (nullableInt.HasValue)
                { 
                    //
                }

    可空类型缩写,因为可空类型比较常用,所以可以使用了一个比较简便的声明方式,可以在简单类型后面加个问号表示可以空类型,如int? op1=5 

    可空类型可以像简单类型一样使用+-*/等运算符来处理值,

    int ? op1=5
    int ? op2=op1*8;

    简单类型可以隐式转换成可空类型,可空类型转换成简单类型必须使用显示转换..如下例子2会隐式转换成可空类型参与运算,运算结果强制转换成int类型。

    int? op1=5
    int op2 =(int)op1*2;

    我们也可以通过可空类型的Value属性进行计算,但要判断是否有值,否则会引发异常

    int? op1 = 5;
    if (op1.HasValue)
    {
          int op2 = op1.Value + 1;
    }

    对于bool?之外的简单可空类型,如果运算式子中有一个为null,则计算结果为null

    int? op1 = null;
     int? op2 = op1 + 1;//null

    对于bool? 可以使用的运算符有&、|.他们的计算结果如下

    op1 op2 op1&op2 op1|op2
    true true true true
    true false false true
    true null null true
    false true false true
    false false false false
    false null false null
    null true null true
    null false false null
    null null null null

    可以这样简单的记忆,对于&运算符有一个false则为false,不管有没有null,其他情况有null皆为null;

    对于|相反,有一个true则为true,不管有没有null,其他情况有null皆为null。

    2.2空接运算符??

    int ?op1=2;
    int ? op2=op1*2??5
    
    等价于
    int ? op1=2;
    int ? op2=op1*2==null?5:op1*2

    如果??前面的表达式的值为null,则赋予??后面表达式的值赋给变量,否则把??前面表达式的值赋值给变量

    3..net提供的泛型

    3.1 List<T>

    使用List<T>可以很轻易的创建自己想要的强类型集合,它的很多方法都已经自动实现了,不必像之前那样从CollectionBase继承,并实现对应的方法。

    创建T类型的对象需要以下语法,T是我们使用的具体类型,比如string。

    List<T> myCollection=new List<T>(); 

    List<T>有3个构造函数,我们可以使用默认的构造函数,也可以传一个支持IEnumerable接口的集合,或者传入一个list的初始容量。

    public List()
    public List(IEnumerable<T> collection)
    public List(int capacity)

    List<T>支持的方法和属性:

    成员 说明
    int Count 返回集合中项的个数
    void Add(T item ) 把一个项添加到集合中
    void AddRange(IEnumerable <T>) 把多个项添加到集合中
    IList<T> AsReadOnly 给集合返回一个只读接口
    int Capacity 获取或者设置集合可以包含的项数。Capicity大于等于Count
    void Clear() 清空所有项
    bool Contains(T item) 确定项item是否包含在集合中
    void CopyTo(T [] array,int index) 把集合复制到数组array中,从数组的索引index开始
    IEnumerator<T>GetEnumerator() 获取一个IEnumerator<T>实例用于foreach循环。注意:返回的接口是强类型化为T的,所以不需要类型转换。
    int Indexof(T item) 获取item的索引
    int Insert(int index,T item) 在index位置插入item项
    bool Remove(T item) 在集合中删除第一个item,并返回true,如果item不存在返回false
    void RemoveAt(int index) 删除index位置的项

    List<T>还有一个Item属性,允许List<T>像数组那样用下标进行访问

    T item=myCollectionOfT[2];

    List<T>例子

        class Animal
        {
            public string name;
            public Animal(string name)
            {
                this.name = name;
            }
    
            public void Feed()
            {
                Console.WriteLine("{0} has been Feeded.", this.name);
            }
        }
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
    
                List<Animal> list = new List<Animal>();
                Animal a = new Animal("Rock");
                Animal b = new Animal("Peter");
                Animal c = new Animal("Tom");
                list.Add(a);
                list.Add(b);
                list.Add(c);
                list.Add(a);
    
                foreach (Animal o in list)
                {
                    o.Feed();
                }
    
                Console.WriteLine(list.IndexOf(a));//返回第一个a的索引,如果集合未包含a返回-1
    
                list.Remove(a);//删除第一个a
                Console.WriteLine(list.IndexOf(a));
                Console.WriteLine("删除后");
                foreach (Animal o in list)
                {
                    o.Feed();
                }
    
                Console.WriteLine("Count:{0},Capacity:{1}", list.Count, list.Capacity);
                    Console.ReadLine();
            }
        }

     3.2对泛型列表进行排序和搜索

    对泛型列表进行排序和其他列表是一样的,其他列表,比如ArrayList可以通过IComparable、IComparer接口进行排序,泛型列表可以通过实现IComparable<T>,IComparer<T>进行排序,唯一的区别在于IComparable<T>,IComparer<T>是强类型化的,它只针对特定类型进行排序。下表列出了他们的区别:

    泛型方法 非泛型方法 区别
    int IComparable<T>.CompareTo(T otherObj) int IComparable.CompareTo(object obj) 泛型版本是强类型化的
    int IComparer<T>.CompareTo(T otherObj) int IComparer.CompareTo(object obj) 泛型版本是强类型化的
     //实现IComparable<Vector>的好处是比较时传入参数是强类型化的,不用类型转换
        class Vector:IComparable,IComparable<Vector>
        {
            public double? R = null;//大小
            public double? Theta = null;//方向,单位度
    
            //弧度
            public double? ThetaRadians
            { 
                //度和弧度转换180度=pi弧度,
                get
                {
                    return (Theta * Math.PI / 180);
                }
            }
    
            public Vector(double? r, double? theta)
            {
                if (r < 0)
                {
                    r = -r;
                    theta += 180;
                }
    
                Theta = theta % 360;
                R = r;
            }
    
            public override string ToString()
            {
                string rString = R.HasValue ? R.ToString() : "null";
                string thetaString = Theta.HasValue ? Theta.ToString() : "null";
                return string.Format("{0},{1}",rString,thetaString);
            }
    
            public int CompareTo(object obj)
            {
                if (this.R > ((Vector)obj).R)
                    return -1;
                else if (this.R < ((Vector)obj).R)
                    return 1;
                return 0;
            }
    
            public int CompareTo(Vector other)
            {
                if (this.R > other.R)
                    return 1;
                else if (this.R < other.R)
                    return -1;
                return 0;
            }
    
        }
    
        class Vectors:List<Vector>
        {
            public Vectors()
            { }
    
            public Vectors(IEnumerable<Vector> initialItem)
            {
                AddRange(initialItem);
            }
        }
    
        class VectorDelegates
        {
            public static int Compare(Vector x, Vector y)
            {
                if (x.R > y.R)
                    return 1;
                else if (x.R < y.R)
                    return -1;
                return 0;
    
            }
    
            public static bool TopRightQuadrant(Vector target)
            {
                if (target.Theta >= 0.0 && target.Theta <= 90.0)
                    return true;
                else
                    return false;
            }
        }
    
        class VectorComparer :IComparer,IComparer<Vector>
        {
            public static VectorComparer Default= new VectorComparer();
    
    
            public int Compare(object x, object y)
            {
                return (int)(((Vector)x).Theta-((Vector)y).Theta);
            }
    
            public int Compare(Vector x, Vector y)
            {
                return (int)(x.Theta-y.Theta);
            }
        }
    
        class Program
        {
    
            static void Main(string[] args)
            {
                Vectors route = new Vectors();
                route.Add(new Vector(2.0, 90.0));
                route.Add(new Vector(1.0, 180.0));
                route.Add(new Vector(0.5, 45.0));
                route.Add(new Vector(2.0, 90.0));
                route.Add(new Vector(2.5, 315.0));
    
                //使用Vector默认的比较方法排序,如果同时实现了IComparable,IComparable<Vector>两个接口,比较时优先使用强类型化的方法
                //所以以下排序会使用public int CompareTo(Vector other)方法而不是public int CompareTo(object obj)排序
                route.Sort();
    
                Console.WriteLine("默认排序,按大小降序");
                foreach (Vector v in route)
                {
                    Console.WriteLine(v.ToString());
                }
    
                Console.WriteLine("使用泛型委托排序,按大小升序");
                Comparison<Vector> sorter = new Comparison<Vector>(VectorDelegates.Compare);
                route.Sort(sorter);
                foreach (Vector v in route)
                {
                    Console.WriteLine(v.ToString());
                }
    
                Console.WriteLine("使用IComparer<T>接口排序,按角度大小升序");
                route.Sort(VectorComparer.Default);
                foreach (Vector v in route)
                {
                    Console.WriteLine(v.ToString());
                }
    
                Console.WriteLine("恢复默认排序后使用IComparer<T>接口,对前面三项排序,按角度大小升序");
                route.Sort();
                route.Sort(0, 3, VectorComparer.Default);
                foreach (Vector v in route)
                {
                    Console.WriteLine(v.ToString());
                }
    
                Console.WriteLine("使用泛型委托Predicate<T>查找所有角度在0到90之间的向量");
                Predicate<Vector> searcher = new Predicate<Vector>(VectorDelegates.TopRightQuadrant);
                Vectors topRightQuadrant =new Vectors( route.FindAll(searcher));
                foreach (Vector v in topRightQuadrant)
                {
                    Console.WriteLine(v.ToString());
                }
    
                route[0].CompareTo(route[1]);
    
                Console.ReadLine();
            }
        }
    View Code

    除了使用泛型接口IComparable<T>和IComparer<T>对泛型列表进行排序,上面的例子中还使用了泛型委托Comparison<T>对集合排序,它的定义如下

    public delegate int Comparison<in T>(T x, T y);

    它表示一个返回类型为int ,有两个T类型参数的委托,使用时要传入一个方法签名一样的方法

    还可以使用泛型委托Predicate<in T>查找符合条件的项,它的定义如下

    public delegate bool Predicate<in T>(T obj);

     3.3Dictionary<K,V>

    使用Dictionary<k,v>可以创建类型为k,和V的键值对集合,实例化之后可以像继承自DictionaryBase的类那样执行相同的操作。添加到接口Dictionary<k,v>的键必须唯一,否则会抛出ArgumentException异常。一般情况下,如果K是简单类型,比如int,string会判断值是否相等,如果是引用类型(除string)会判断两个对象的引用是否相等。如果要改变判断的规则可以给构造函数传入一个IEqualityComparer<TKey> comparer接口对象,如下使用不区分大小写的方法比较字符串

    Dictionary<string, int> things = new Dictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);

    对于自己的类,我们可以继承IEqualityComparer<T>接口改变判断规则,IEqualityComparer<T>有两个比较方法,一般情况下先使用int GetHashCode(T obj)方法判断,如果两个比较对象返回值是相等的,则进一步用bool Equals(T x, T y)方法判断两个对象是否相等。

        // 摘要:
        //     定义方法以支持对象的相等比较。
        //
        // 类型参数:
        //   T:
        //     要比较的对象的类型。
        public interface IEqualityComparer<in T>
        {
            // 摘要:
            //     确定指定的对象是否相等。
            //
            // 参数:
            //   x:
            //     要比较的第一个类型为 T 的对象。
            //
            //   y:
            //     要比较的第二个类型为 T 的对象。
            //
            // 返回结果:
            //     如果指定的对象相等,则为 true;否则为 false。
            bool Equals(T x, T y);
            //
            // 摘要:
            //     返回指定对象的哈希代码。
            //
            // 参数:
            //   obj:
            //     System.Object,将为其返回哈希代码。
            //
            // 返回结果:
            //     指定对象的哈希代码。
            //
            // 异常:
            //   System.ArgumentNullException:
            //     obj 的类型为引用类型,obj 为 null。
            int GetHashCode(T obj);
        }

    只用int GetHashCode(T obj)判断的情况,如下例子中,我们创建两个同名的Animal对象,但是这两个对象属于不同的引用,obj.GetHashCode()返回的是不同的值,所以不管Equals方法怎么实现,程序都会认为cow和cow2是两个不同的对象。

        class Animal
        {
            protected string name;
            public string Name
            {
                get { return name; }
                set { name = value; }
            }
    
            public Animal()
            {
                name = "The animal has no name.";
            }
    
            public Animal(string newName)
            {
                name = newName;
            }
    
            public void Feed()
            {
                Console.WriteLine("{0}has been fed.", name);
            }
    
        }
    
        class AnimalComparer : IEqualityComparer<Animal>
        {
            public static AnimalComparer Default = new AnimalComparer();
    
           
            public bool Equals(Animal x, Animal y)
            {
                if (Comparer.Default.Compare(x.Name, y.Name) == 0)
                    return true;
                else
                    return false;
    
                //return x == y;
            }
    
            public int GetHashCode(Animal obj)
            {
                return obj.GetHashCode();
            }
        }
    
        class Program
        {
    
            static void Main(string[] args)
            {
               
                Dictionary<Animal, int> a = new Dictionary<Animal, int>(AnimalComparer.Default);
                Animal cow = new Cow("cow");
                Animal cow2 = new Cow("cow");
    
                a.Add(cow, 1);
                a.Add(cow2, 1);
              
                Console.ReadLine();
            }
        }

    两个方法都使用的情况,我们把GetHashCode方法稍微改一下,同名的对象返回相同的值,此时调试发现,程序比较GetHashCode完方法之后又用Equals方法进行了比较,因为两次比较是相同的,所以添加cow2时程序报错

        class Animal
        {
            protected string name;
            public string Name
            {
                get { return name; }
                set { name = value; }
            }
    
            public Animal()
            {
                name = "The animal has no name.";
            }
    
            public Animal(string newName)
            {
                name = newName;
            }
    
            public void Feed()
            {
                Console.WriteLine("{0}has been fed.", name);
            }
    
        }
    
        class AnimalComparer : IEqualityComparer<Animal>
        {
            public static AnimalComparer Default = new AnimalComparer();
    
           
            public bool Equals(Animal x, Animal y)
            {
                if (Comparer.Default.Compare(x.Name, y.Name) == 0)
                    return true;
                else
                    return false;
    
                //return x == y;
            }
    
            public int GetHashCode(Animal obj)
            {
                return obj.Name.GetHashCode();
            }
        }
            static void Main(string[] args)
            {
               
                Dictionary<Animal, int> a = new Dictionary<Animal, int>(AnimalComparer.Default);
                Animal cow = new Animal("cow");
                Animal cow2 = new Animal("cow");
    
                a.Add(cow, 1);
                a.Add(cow2, 1);
              
                Console.ReadLine();
            }

     

    我们还可以给构造函数传递一个支持IDictionary<K,V>的集合,或者指定集合的大小Capacity,Dictionary<k,v>有以下构造函数,可以根据情况使用

    可以使用集合的Keys和Values属性迭代集合中的键和值,也可以使用KeyValuePair<K,V>迭代集合中的每个项,使用Dictionary[K]访问对象某项的值

        class Program
        {
    
            static void Main(string[] args)
            {
               
                Dictionary<Animal, int> a = new Dictionary<Animal, int>();
                Animal cow = new Animal("cow");
                Animal cow2 = new Animal("cow");
    
                a.Add(cow, 1);
                a.Add(cow2, 1);
    
                foreach (Animal k in a.Keys)
                {
                    Console.WriteLine(k.Name);
                }
    
    
                foreach (int v in a.Values)
                {
                    Console.WriteLine(v);
                }
    
                foreach (KeyValuePair<Animal,int> d in a)
                {
                    Console.WriteLine(d.Value);
                }
    
                int value = a[cow];
                Console.ReadLine();
            }
        }
  • 相关阅读:
    数据结构-循环队列程序演示
    C++进阶:新人易入的那些坑 --1.常量、常指针和指针常量
    this.$confirm的用法
    属性或方法“degreeList”不是在实例上定义的,而是在渲染期间引用的。通过初始化该属性,确保该属性是反应性的,无论是在data选项中,还是在基于类的组件中
    CSS清除浮动
    react里的高阶组件
    map和forEach的区别
    hash和history两种模式的区别
    js原型链的理解
    for..in,for..of 和forEach的区别
  • 原文地址:https://www.cnblogs.com/lidaying5/p/10609693.html
Copyright © 2020-2023  润新知