• 用人类自然的语言说泛型


    泛型
    一、什么是泛型?
    通过泛型可以定义类型安全类,而不会损害类型安全、性能或工作效率

    二、实例化泛型
    1、可以使用任何类型来声明和实例化
    2、申明和实例话都必须用一个特定的类型来代替一般类型T
    3、例子:
    //原来写法
    Public class Stack
    {
    object[] m_Items;
    public void Push(object item)
    {...}
    public object Pop()
    {...}
    }
    Stack stack = new Stack();
    stack.Push(1);
    int number = (int)stack.Pop();

    //有了泛型后
    Public class Stack <T>
    {
    T[] m_Items;
    public void Push(T item)
    {...}
    public T Pop()
    {...}
    }
    Stack <int> stack = new Stack <int> ();
    stack.Push(1);
    int number = (int)stack.Pop();

    三:泛型的好处
    1、一次性的开发、测试和部署代码,通过任何类型来重用它
    2、编译器支持和类型安全
    3、不会强行对值类型进行装箱和取消装箱,或者对引用类型进行向下强制类型转换,所以性能得到显著提高。
    注:值类型大概可以提高200%,引用类型大概为100%

    四:多个泛型
    1、单个类型可以定义多个泛型

    五:泛型别名
    1、在文件头部使用using 为特定类型取别名,别名作用范围是整个文件
    2、例子
    using List = LinkedList <int,string> ;
    class ListClient
    {
    static void Main(string[] args)
    {
    List list = new List();
    list.AddHead(123, "AAA ");
    }
    }

    五:泛型约束
    (1)、派生约束
    如:
    public class LinkedList <K,T> where K:IComparable
    {
    T Find(K key)
    {
    if (str.Key.CompareTo(key) == 0)//只有实现这个接口才可比较
    }
    }

    注意:
    1、所有的派生约束必须放在类的实际派生列表之后
    如:public class LinkedList <K,T> :IEnumerable <T> where K:IComparable <K>
    {...}
    2、一个泛型参数上可以约束多个接口(用逗号分隔)
    public class LinkedList <K,T> where K:IComparable <K> ,IConvertible
    3、在一个约束中最多只能使用一个基类
    4、约束的基类不能是密封类或静态类
    5、不能将System.Delegate或System.Array约束为基类
    6、可以同时约束一个基类以及一个或多个接口,但是该基类必须首先出现在派生约束列表中。
    7、C#允许你将另一个泛型参数指定为约束
    public class MyClass <T,U> where T:U
    {...}
    8、可以自己定义基类或接口进行泛型约束
    9、自定义的接口或基类必须与泛型具有一致的可见性

    (2)、构造函数约束
    如:
    class Node <K,T> where T:new()
    {
    }
    注意:
    1、可以将构造函数的约束和派生约束结合起来,前提是构造函数的约束出现在约束列表中的最后

    (3)、引用/值类型约束
    1、可以使用struct约束将泛型参数约束为值类型(如int、bool、enum),或任何自定义结构
    2、同样可以使用class约束将泛型参数约束为引用类型
    3、不能将引用/值类型约束与基类约束一起使用,因为基类约束涉及到类
    4、不能使用结构和默认构造函数约束,因为默认构造函数约束也涉及到类
    5、虽然您可以使用类和默认构造函数约束,但是这样做没有任何价值
    6、可以将引用/值类型约束与接口约束组合起来,前提是引用/值类型约束出现在约束列表的开头

    六:泛型和强制类型转换
    1、C#编译器只允许将泛型参数隐式转换到Object或约束指定的类型
    如:
    interface IS{...}
    class BaseClass{...}
    class MyClass <T> where T:BaseClass,IS
    {
    void SomeMethod(T t)
    {
    IS obj1 = t;
    BaseClass obj2 = t;
    object obj3 = t;
    }
    }
    2、编译器允许你将泛型参数显示强制转换到其他任何借口,但不能将其转换到类
    interface IS{...}
    class SomeClass{...}
    class MyClass <T> //没有约束
    {
    void SomeMethod(T t)
    {
    IS obj1 = (IS)t; //可以
    SomeClass obj2 = (SomeClass)t //不可以
    }
    }
    3、可以使用临时的Object变量,将泛型参数强制转换到其他任何类型
    class SomeClass{...}
    class MyClass <T>
    {
    void SomeMethod(T t)
    {
    object temp = t;
    SomeClass obj = (SomeClass)temp;//可以
    }
    }
    注意:这里只是告诉你这样写是可以的,但是要不要这样写?不要这样写,因为如果t确实没有继承SomeClass编译没错但是运行就会出错
    4、解决上面强制转换问题,可以使用is和as运算符进行判断
    public class MyClass <T>
    {
    public void SomeMethod <T t>
    {
    if (t is int ){...}
    if (t is LinkedList <int,string> ){...}
    //如果泛型参数的类型是所查询的类型,则is运算符返回true
    string str = t as string;
    //如果这写类型兼容,则as将执行强制类型转换,否则将返回null
    if (str != null){...}
    LinkedList <int,string> list = t as LinkedList <int,string> ;
    if (list != null){...}
    }
    }

    七:继承和泛型
    1、在从泛型基类派生,可以提供类型实参,而不是基类泛型参数
    public class BaseClass <T> {...}
    public class SubClass:BaseClass <int>
    2、如果子类是泛型,而非具体的类型实参,则可以使用子类泛型参数作为泛型基类的指定类型
    public class BaseClass <TT> {...}
    public class SubClass <T> :BaseClass <T> {...}
    3、在使用子类泛型参数时,必须在子类级别重复在基类级别规定的任何约束
    4、基类可以定义其签名使用泛型参数的虚礼方法,在重写它们时,子类必须在方法签名中提供相应的类型。
    如:
    public class BaseClass <T>
    {
    public virtual T SomeMethod()
    {...}
    }
    public class SubClass:BaseClass <int>
    {
    public override int SomeMethod()
    {...}
    }
    5、如果该子类是泛型,则它还可以在重写时使用它自己的泛型参数
    public class SubClass <T> :BaseClass <T>
    {
    public override T SomeMethod()
    {...}
    }
    6、你可以定义泛型接口、泛型抽象类,甚至泛型抽象方法。
    7、不能对泛型参数使用+或+=之类的运算符
    public class Calculator <T>
    {
    public T Add (T arg1,T arg2)
    {
    return arg1 + arg2;//错误
    }
    }
    但是我们可以通过泛型抽象类、接口来实现在个功能,因为实现泛型抽象类、接口我们就已经明确传一个参数了,就可以执行诸如+这样的操作。

    八:泛型方法
    1、方法可以定义特定于其执行范围的泛型参数
    public class MyClass <T>
    {
    public void MyMethod <X> (X x)
    {...}
    }
    2、即使各包含类根本不使用泛型,你也可以定义方法特定的泛型参数
    public class MyClass
    {
    public void MyMethod <T> (T t)
    {...}
    }
    注意:该功能只使用于方法,属性,索引器只能使用在类的作用范围中定义的泛型参数。
    3、调用泛型方法
    MyClass obj = new MyClass();
    obj.MyMethod <int> (3);
    也可以这样:
    MyClass obj = new MyClass();
    obj.MyMethod(3); //该功能称为泛型推理
    4、泛型方法也可以有自己的泛型参数约束
    pubic class MyClass
    {
    public void SomeMethod <T> (T t) where T:IComparable <T>
    {...}
    }
    5、子类方法实现不能重复在父级别出现的约束
    public class BaseClass
    {
    public virtual void SomeMethod <T> (T t)where T:new()
    {...}
    }
    pubic class SubClass:BaseClass
    {
    public override void SomeMethod <T> (T t)//不能再有约束
    {...}
    }
    6、静态方法
    静态方法可以定义特定的泛型参数和约束
    public class MyClass <T>
    {
    public static T SomeMethod <X> (T t,X x)
    {...}
    }
    int number = MyClass <int> .SomeMethod <string> (3, "AAA ");
    或者:int mumber = MyClass <int> .SomeMethod(3, "AAA ");

    九:泛型委托
    1、在某个类中定义的委托可以利用该类的泛型参数
    2、委托也可以定义自己的泛型参数

    http://topic.csdn.net/u/20070903/17/0B06562A-19B9-4BAE-85C3-0FB1AE1D6037.html

  • 相关阅读:
    41 快速的复制一张表
    4 cdh 5.12 centos 6.10三节点安装
    40 insert语句的锁
    oracle 11g 数据库恢复技术 ---02 控制文件
    05 使用bbed跳过归档恢复数据文件
    Springboot 配置文件与对象之间进行映射之@ConfigurationProperties
    @ConditionalOnProperty来控制Configuration是否生效
    Oracle 服务名/实例名,Service_name 和Sid的区别
    @Value中冒号的作用
    springboot读取配置不存在报错
  • 原文地址:https://www.cnblogs.com/zzxap/p/2175963.html
Copyright © 2020-2023  润新知