泛型

泛型

泛型允许你在编译时实现类型安全。它们允许你创建一个数据结构而不限于一特定的数据类型。然而,当使用该数据结构时,编译器保证它使用的类型与类型安全是相一致的。泛型提供了类型安全,但是没有造成任何性能损失和代码臃肿

泛型优势

  • 类型安全

  • 避免装箱拆箱带来的性能损耗

  • 二进制代码重用

非泛型集合

var arr1 = new ArrayList();
arr1.Add(10);//Add方法参数值是object类型,这里将int->object
arr1.Add("wang");//Add方法参数值是object类型,这里将int->object
foreach (int item in arr1)//这里如果用int类型遍历集合,就挂掉了
{
    Console.WriteLine(item);//读取时要进行拆箱
}

 泛型集合

/*泛型集合在使用时定义好类型,之后就不存在装箱拆箱;
 * 因为已经定义当前集合只能存入int类型,也就不能存入其他类型
 * 编译时就会报错,错误应及早发现
 */
var arr2 = new List<int>();
arr2.Add(10);
foreach (var item in arr2)
{
    Console.WriteLine(item);
} 

通过参数化类型来实现在同一份代码上操作多种数据类型,利用“参数化类型”将类型抽象化,从而实现灵活的复用。每个集合的详细规范可以在System.Collection.Generic名称空间下找到

泛型的适用场景

打印int,string类型参数

    class Print
    {
        public static void PrintInt(int parameter)
        {
            Console.WriteLine("This is {0},parameter={1},type={2}",
               typeof(Print).Name, parameter.GetType().Name, parameter);
        }

        public static void PrintString(string parameter)
        {
            Console.WriteLine("This is {0},parameter={1},type={2}",
               typeof(Print).Name, parameter.GetType().Name, parameter);
        }
    }

如果需要继续添加打印double或者其他类型参数就需要继续添加方法;使用继承object类处理,因为object是所有类型的基类

    class Print
    {
        public static void PrintObject(object parameter)
        {
            Console.WriteLine("This is {0},parameter={1},type={2}",
               typeof(Print).Name, parameter.GetType().Name, parameter);
        }
    }

 这么做看起来解决了这个问题,考虑下面的情形(没有实际意义,仅做学习参考)

class Print
    {
        public static void PrintObject(object parameter)
        {
            int str = 0;
            str += (int)parameter;
            Console.WriteLine("This is {0},parameter={1},type={2}",
               typeof(Print).Name, parameter.GetType().Name, str);
        }
    }
class Program
    {
        static void Main(string[] args)
        {
            Print.PrintObject(1);
            Print.PrintObject(true);
            Console.ReadKey();
        }
    }

再比如这样

            ArrayList list = new ArrayList();
            list.Add(50);
            list.Add("哈哈");
//如果使用整数对象来使用foreach语句进行遍历的话,当编译器接受到代码,但是因为集合中的所有元素都不是整数,所以会导致运行时异常
foreach (int i in list) { Console.WriteLine(i); }

当传值为int类型1时,代码是可以正常运行的,但传值为true就会造成类型转换错误,并且编译时并没有获取到这个错误,这说明当前的方法并不是类型安全的;而且使用object类会造成大量的装箱/拆箱操作,这对性能也有很大损耗

这种时候就可以考虑使用泛型来实现

泛型方法

    class Print
    {
        /// <summary>
        /// Object方法处理打印各种类型值
        /// </summary>
        /// <param name="parameter"></param>
        public static void PrintObject(object parameter)
        {
            Console.WriteLine("This is {0},parameter={1},type={2}",
               typeof(Print).Name, parameter.GetType().Name, parameter);
        }
        
        /// <summary>
        /// 泛型方法处理打印各种类型值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tParameter"></param>
        public static void PrintT<T>(T tParameter)
        {
            Console.WriteLine("This is {0},parameter={1},type={2}",
              typeof(Print).Name, tParameter.GetType().Name, tParameter);
        }
    }

 泛型类

    class Program
    {
        static void Main(string[] args)
        {
            Print<int> iPrint = new Print<int>();
            iPrint.t = 123;
            Print<string> sPrint = new Print<string>();
            sPrint.t = "123";
            Console.ReadKey();
        }
    }
    class Print<T>
    {
        public T t;
    }

default

泛型default用于将泛型类型初始化为null或0

T t=default(T);

 泛型接口

    interface IPrint<T>
    {
        T GetT(T t);
    }

泛型委托

public delegate void Print<T>(T t);

泛型在声明的时候可以不指定具体的类型,但是在使用的时候必须指定具体类型

 Print<int> iPrint = new Print<int>();//这里的类型是int

如果存在继承并且子类也是泛型的,那么继承的时候可以不指定具体类型

    class Print<T>
    {
        public T t;
    }
    
//这里继承时没有指定泛型类型
class ConsolePrint<T> : Print<T> { }

同上,类实现泛型接口也是如此

泛型约束

 T:calss 类型参数必须是引用类型,包括任何类、接口、委托或数组类型
 T:stauct 类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型
 T:new() 类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定
 T:<基类名> 类型参数必须是指定的基类或派生自指定的基类
 T:<接口名称> 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的
T1:T2 类型T1派生自泛型类型T2,该约束也被称为裸类型约束
    class Person
    {
        public static void PintSayHello()
        {

        }
    }

    interface ISport
    {
        void Sport();
    }

    /// <summary>
    /// 约束T必须是Person类型或者是Person的子类并且实现了ISport接口且T类型中必须有无参构造函数
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class Show<T> where T : Person, ISport, new()
    {

    }

    /// <summary>
    /// 约束T必须是引用类型并且实现了ISport接口且T类型中必须有无参构造函数
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class Show2<T> where T : class, ISport, new()
    {

    }

    /// <summary>
    /// 约束T必须是值类型并且实现了ISport接口
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class Show3<T> where T : struct, ISport
    {

    }

注意:泛型约束可以同时约束多个,有多个泛型约束时,new()约束一定是在最后

参考:

https://www.cnblogs.com/dotnet261010/p/9034594.html

https://www.cnblogs.com/yilezhu/p/10029782.html

https://www.cnblogs.com/aehyok/p/3384637.html

原文地址:https://www.cnblogs.com/GnailGnepGnaw/p/10606944.html