[C#基础] 泛型

为什么泛型?

在泛型中,最重要的应用便是集合类,因此我们模拟一个简单的集合类

对于上述示例,可以有如下应用

  从上可看出,自定义的代码太丑陋了,只能用于string类型。 当然我们可以用object作为参数类型,然而深入分析则会发现有些问题仍然存在,这种方式还会有性能问题,值类型Add到集合时必然存在装箱,而将元素赋值给一个值类型变量时,又存在拆箱,这样的性能损失在操作大量元素的时候格外明显!

  正是这个问题的存在,所以泛型登场了。重新实现MyArray类型,体验泛型的好处

 

自定义的泛型集合类,能够轻松实现对任意类型的管理和操作

泛型基础

泛型类

上述代码已经演示了泛型类,我们发现定义一个泛型类和定义一个非泛型类并没有太大的区别,主要的不同在于,类型参数化。类型定义时,将指定类型形参(通常用T表示),紧随类名,并包含在<>内。

泛型类的声明

上述代码将创建一个值类型对象myArr,并调用构造器完成初始化。int作为T的类型实参在JIT编译时所有T将被替换为int,因此_items内部数组将保存int类型的数组元素。

泛型类型的声明也可以包括多个类型形参,如

约束

约束,指在定义泛型时,对于能够实例化类型参数的类型所做的限制,这种限制能保证类型参数在一定的目标范围。

约束通过where字句来实现,多个约束之间用逗号隔开,通常情况下,在设计泛型类或泛型方法时,如果要实现一个object类不支持的任何方法,则需要对参数类型进行约束。

约束主要包括,构造器约束,值类型约束,引用类型约束,基类约束,接口约束,具体情况为:

  • T:new(),表示类型参数必须具有公共无参构造函数,有多个约束存在时,必须把new()放在最后面。
  • T:struct,表示类型参数必须是值类型。
  • T:class,表示类型参数必须是引用类型。
  • T:基类名,表示类型参数必须是基类或及其派生类。但是不能既指定基类约束又指定class
  • T:接口名,表示类型参数必须是指定的接口,或实现了该接口的接口,可以同时定义多个接口约束,约束接口也可以是泛型的。

泛型方法

泛型方法可以存在于泛型类,也可以存在于非泛型类中

可以将类型参数作为某个方法的参数,返回值或者局部变量,该类型参数可能并不被整个类所需要,而更明确的用于某个方法,例如

方法重载

由此可见,泛型方法可以以类型参数形成重载。

然而泛型方法重载,存在值得注意的问题:由于类型参数在编译期并不确定,所以重载检查是在实例方法被调用时才发生,而不是在泛型类本身编译时检查,例如下面的泛型类可以安全通过编译

然而在调用时,将会给出调用不明确的编译错误

类型推断

泛型方法的调用,可以用更简洁的方式来实现,例如

 myArr.ShowInfo("s");

这种机制由编译器支持,称为:类型推断。表示编译器在调用时根据方法参数来推断类型参数,从而决定使用的泛型方法。因此,这种方式不适用于没有参数的方法,同时编译器会优先选择显示的匹配调用,例如上面所示,将会调用非泛型ShowInfo方法,而非泛型方法ShowInfo<TData>方法,除非以显示方式调用来是实现。

myArr.ShowInfo<string>("s");

泛型接口

 泛型接口允许我们编写参数和接口成员返回类型是泛型类型参数的接口

如下代码声明了一个泛型接口,泛型类Sample实现了这个泛型接口,main实例化了两个泛型类,一个int 一个string

 class Program
    {
        static void Main(string[] args)
        {
            var testInt = new Sample<int>();
            var testString = new Sample<string>();
            Console.WriteLine(testInt.ReturnIt(1));
            Console.WriteLine(testString.ReturnIt("aa"));

        }
    }
    interface IMyIfc<T>
    {
        T ReturnIt(T value);
    }
    class Sample<T> : IMyIfc<T>
    {

        public T ReturnIt(T value)
        {
            throw new NotImplementedException();
        }
    }
View Code

使用泛型接口的示例

如下示例演示了泛型接口的两个额外的能力

  • 与其他泛型相似,实现不同类型参数的泛型接口是不同的接口
  • 可以在非泛型类中实现泛型接口

例如下面的代码与前面的相似,但在这里Sample是个非泛型类,它实现了两个IMyIfc实例,一个int类型,一个string类型

class Sample : IMyIfc<int>, IMyIfc<string>
    {

        public int ReturnIt(int value) //实现int类型
        {
            throw new NotImplementedException();
        }

        public string ReturnIt(string value) //实现string类型
        {
            throw new NotImplementedException();
        }
    }

泛型接口的实现必须唯一

实现泛型接口时,必须保证类型实参组合不会在类型中产生两个重复的接口

典型的泛型接口

泛型委托

泛型委托支持在返回值和参数上应用类型参数,泛型委托能够有效避免值类型实例传给一个回调方法时的装箱处理。

如下代码实现了一个泛型委托的简单示例

   class Program
    {
        //声明一个泛型委托
        public delegate string MyGenericDelegate<T>(T t);

        //实现两个测试方法
        public static string GetString(string s)
        {
            return s;
        }

        public static string GetInt(int i)
        {
            return i.ToString();
        }
        static void Main(string[] args)
        {
            //实现测试代码
            MyGenericDelegate<string> myStrDel = new MyGenericDelegate<string>(GetString);
            Console.WriteLine(myStrDel("aa"));

            MyGenericDelegate<int> myIntDel=new MyGenericDelegate<int>(GetInt);
            Console.WriteLine(myIntDel(1));
          
        }
    }
View Code

实用Func泛型委托的示例

 class Sample
        {
            public static string PringStr(string s1, string s2)
            {
                return s1 + s2;
            }
        }
        static void Main(string[] args)
        {
            var myDel = new Func<string, string, string>(Sample.PringStr);
            Console.WriteLine(myDel("1","2"));
          
        }
View Code
原文地址:https://www.cnblogs.com/Nomads/p/4864276.html