• 深入理解C#中的泛型(一)


    为什么要有泛型?

      请大家思考一个问题:由你来实现一个最简单的冒泡排序算法。假设没有使用泛型的经验。可能会毫不犹豫的写出下面代码:

    public class SortHelper
        {
            //參数为int数组的冒泡排序
            public void BubbleSort(int[] array)
            {
                int length = array.Length;
    
                for (int i = 0; i <= length - 2; i++)
                {
                    for (int j = length - 1; j >= 1; j--)
                    {
                        //对两个元素进行交换
                        if (array[j] < array[j - 1])
                        {
                            int temp = array[j];
                            array[j] = array[j - 1];
                            array[j - 1] = temp;
                        }
                    }
                }
            }
        }
      如今通过对这个程序进行一个简单的測试:
          static void Main(string[] args)
            {
                #region  没有泛型前的演示
                SortHelper sorter = new SortHelper();
    
                int[] arrayInt = { 8, 1, 4, 7, 3 };
    
                //对int数组排序
                sorter.BubbleSort(arrayInt);
    
                foreach (int i in arrayInt)
                {
                    Console.Write("{0} ", i);
                }
    
                Console.ReadLine();
                #endregion
            }
      我们发现它执行良好,欣喜的觉得这便是最好的解决方式了。直到不久后,须要对一个byte类型的数组进行排序。而上面的排序算法仅仅能接受一个int类型的数组。C#是一个强类型的语言。无法在一个接受int数组类型的地方传入一个byte数组。只是没关系,如今看来最快的解决方法是把代码复制一遍,然后将方法的签名改一下:
     public class SortHelper
        {
            //參数为byte数组的冒泡排序
            public void BubbleSort(byte[] array)
            {
                int length = array.Length;
    
                for (int i = 0; i <= length - 2; i++)
                {
                    for (int j = length - 1; j >= 1; j--)
                    {
                        //对两个元素进行交换
                        if (array[j] < array[j - 1])
                        {
                            byte temp = array[j];
                            array[j] = array[j - 1];
                            array[j - 1] = temp;
                        }
                    }
                }
            }
        }
      好了,再一次解决这个问题。可通过认证观察我们发现,这两个方法除了传入的參数类型不同外。方法的实现非常相似,是能够进行进一步抽象的。于是我们思考,为什么在定义參数的时候不用一个占位符T取代呢?T是Type的缩写。能够代表不论什么类型,这样就能够屏蔽两个方法签名的差异:    
       public class SortHelper<T>
        {
            //參数为T的冒泡排序
            public void BubbleSort(T[] array)
            {
                int length = array.Length;
    
                for (int i = 0; i <= length - 2; i++)
                {
                    for (int j = length - 1; j >= 1; j--)
                    {
                        //对两个元素进行交换
                        if (array[j] < array[j - 1])
                        {
                            T temp = array[j];
                            array[j] = array[j - 1];
                            array[j - 1] = temp;
                        }
                    }
                }
            }
      通过代码我们能够发现,使用泛型极大的降低了反复代码,使代码更加清爽。

    泛型类就类似于一个模板,能够再须要时为这个模板传入不论什么须要的类型。如今更专业些,为占位符T起一个正式的名称。在.Net中叫做“类型參数”。

    类型參数约束

      实际上,如果执行上面的代码就会发现,它连编译都通只是去,为什么呢?考虑这样一个问题:如果我们自己定义一个类型。名字叫做Book,它包括两个字段:一个是int类型的Price代表书的价格;一个是string类型的Title,代表书的标题:

    public class Book 
        {
            //价格字段
            private int price;
            //标题字段
            private string title;
    
            //构造函数
            public Book() { }
    
            public Book(int price, string title)
            {
                this.price = price;
                this.title = title;
            }
    
            //价格属性
            public int Price
            {
                get { return this.price; }
            }
    
            //标题属性
            public string Titie
            {
                get { return this.title; }
            }
        }
      如今创建一个Book类型的数组,然后使用上面定义的泛型类对它进行排序,代码应该像以下这样:
                Book[] bookArray = new Book[2];
    
                Book book1 = new Book(30, "HTML5解析");
                Book book2 = new Book(21, "JavaScript实战");
    
                bookArray[0] = book1;
                bookArray[1] = book2;
    
                SortHelper<Book> sorterGeneric = new SortHelper<Book>();
                sorterGeneric.BubbleSort(bookArray);
    
                foreach (Book b in bookArray)
                {
                    Console.WriteLine("Price:{0}", b.Price);
                    Console.WriteLine("Title:{0}", b.Titie);
                }
      这时问题来了。既然是排序。就免不了比較大小,那么如今请问:book1和book2谁比較大?张三能够说book1大,由于它的Price是30,;而李四能够说book2大,由于它的Title是“J”开头的,比book1的“H”靠后。说了半天,问题在于不确定按什么规则排序。
      既然不知道,那我们就给Book定义一种排序规则(按价格排序),我们声明一个用于比較的接口:
     public interface IComparable
        {
            int CompareTo(object obj);
        }
      让Book类型实现这个接口:
    public class Book : IComparable
        {
            //CODE:上面的实现略
            public int CompareTo(object obj)
            {
                Book book2 = (Book)obj;
                return this.Price.CompareTo(book2.Price);
            }
        }
      既然我们如今已经让Book类实现了IComparable接口,那么泛型类应该可以工作了吧?不行的,还要记得,泛型类是一个模板类。它对在运行时传递的类型參数是一无所知的。也不会做不论什么的推測。所以须要我们告诉泛型类SortHelper<T>,它所接受的T类型參数必须可以进行比較,也就是说必须实现IComparable接口。我们把对T进行约束这样的行为称:泛型參数约束。

      为了要求类型參数T必须实现IComparable接口,须要像以下这样又一次定义:

    public class SortHelper<T> where T : IComparable
        {
            //參数为T的冒泡排序
            public void BubbleSort(T[] array)
            {
                int length = array.Length;
    
                for (int i = 0; i <= length - 2; i++)
                {
                    for (int j = length - 1; j >= 1; j--)
                    {
                        //对两个元素进行交换
                        if (array[j].CompareTo(array[j - 1]) < 0)
                        {
                            T temp = array[j];
                            array[j] = array[j - 1];
                            array[j - 1] = temp;
                        }
                    }
                }
            }
        }
      上面的定义说明了类型參数T必须实现IComparable接口。否则将无法通过编译。由于如今T已经实现了IComparable接口,而数组array中的成员是T的实例。所以当在array[i]后面点击小数点“.”时,VS能够智能提醒出T是IComparable的成员,也就是CompareTo()方法。

  • 相关阅读:
    mybatis的缓存机制
    mybatis动态SQL
    mybatis关联集合List&分布查询传递多列值
    winrt获取文件MD5码
    在wpf中使用winrt的Toast弹框效果
    winrt控件
    WdatePicker组件不显示
    Thread.Sleep in WinRT
    google 语音api
    UTF8编码转换(C#)
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/7293980.html
Copyright © 2020-2023  润新知