zoukankan      html  css  js  c++  java
  • 第6章 泛型

    1. 概述

    • 泛型可以创建独立于被包含类型的类和方法。
    • 泛型不仅限于类,还可用于接口和方法。
    • 泛型优点:
      • 性能:List<T>类使用时定义类型,故不再进行装箱和拆箱操作,即性能高。
      • 类型安全:使用时定义了类型,因此可以通过编译检测出不符合的类型。
      • 二进制代码重用:定义一次,但可以用许多不同的类型实例化。
      • 代码的扩展
      • 命名约定:
        • 泛型类型的名称用字母T作为前缀
        • 没有特殊要求,且只使用一个泛型类型,就可以用字符T作为泛型类型的名称
        • 如有特殊要求(如实现接口或派生自基类),或使用两个或以上泛型类型,就使用描述性的名称

                                   public delegate void EventHandler<TEventArgs>(objcet sender, TEventArgs e);

                                   public delegate TOutput Converter<TInput, TOutput>(TInput from);

                                   public class SortedList<TKey, TValue> {}

    2. 泛型类的功能

    (1) 默认值:default关键字

    • 不能把null赋予泛型类型 => 原因是泛型类型既可以实例化为引用类型,又可以实例化为值类型。而null只能用于引用类型。
    • default关键字,将null赋予引用类型,将0赋予值类型。
    public T GetDocument()
    {
        T doc = defalut(T);
        lock(this)
        {
              doc = documentQueue.Dequeue();
        }
        return doc;
    }    

     (2) 约束:

    • 泛型类需要调用泛型类型中的方法,必须添加约束。
    • 泛型支持的约束类型

    • 可以合并多个约束,如where T : IFoo,new() 即类型T必须实现IFoo接口,且必须有一个默认构造函数
    • 给泛型类型添加约束时,最好包含泛型参数名称的一些信息
    // TDocument 来代替 T
    // 对于编译器而言,参数名不重要,但更具有可读性
    // 即TDocument类型必须实现IDocument接口
    public class DocumentManager<TDocument>
          where TDocument : IDocument
    {
         //...
    }

     (3) 继承

    • 泛型类型可以实现泛型接口(IEnumerable<T>),也可以派生自一个类
    • 派生类可以是泛型类或非泛型类
    public class Base<T>
    {
    }
    
    // 派生自泛型基类
    public class Derived<T> : Base<T>
    {
    }
    // 派生自指定基类的类型
    public class Derived<T> : Base<string>
    {
    }
    • 还可以创建一个部分的特殊操作
    public class Query<TRequest, TResult>
    {
    }
    public StringQuery<TRequest> : Query<TRequest, TResult>
    {
    }

    (4) 静态成员:泛型类的静态成员只能在类的一个实例中共享。

    // StaticDemo<T>类包含静态字段X
    public classs StaticDemo<T>
    {
        public static int x;
    }
    // 同时对不同类型使用泛型类
    StaticDemo<string>.x = 4;
    StaticDemo<int>.x =5;
    WriteLine(StaticDemo<string>.x); // writes 4

     3. 泛型接口

    (1) 协变与抗变:指对参数和返回值的类型进行转换

    • 协变:参数类型的转换。可以理解成:父类 -> 子类。父类的对象用子类替换,也可以理解成子类当父类用。例如,函数Act的输入参数为object类型,实际操作中我们可以将string类型的对象传给函数。
    private void button1_Click(object sender, EventArgs e)
    {
        string str = "这是一个string类型的实例, 函数Act的参数为object, 这里有协变的应用";
        Act(str);
    }
    
    void Act(object obj)
    {
        return ;
    }
    •  抗变:方法的返回类型的转换。可以理解成:子类 -> 父类。子类的对象用父类替换,也可以理解成父类当子类用。抗变也常常翻译为逆变。例如,函数Func的返回类型为string,我们可以将返回的值赋给object对象。
    private void button2_Click(object sender, EventArgs e)
    {
        //注意这里:Func的返回类型为string, obj的类型为object, string类型继承自object
        object obj = Func();
    }
    
    string Func()
    {
        return "这里有抗变的应用";
    }

    (2) 泛型接口的协变:用out关键字标注,泛型接口就是协变的。即在接口实现代码里面,T只能用作返回类型,不能用作参数类型。

    (3) 泛型接口的抗变:用in关键字标注,泛型接口就是抗变的。即在接口实现代码里面,T用作方法的输入。

    //泛型接口支持协变、逆变和不支持协变、逆变的对比
    //1-定义一个接口IFoo,既不支持协变,也不支持逆变。
        interface IFoo<T>
        {
            void Method1(T param);
            T Method2();
        }
        //实现接口IFoo
        public class FooClass<T> : IFoo<T>
        {
            public void Method1(T param)
            {
                Console.WriteLine(default(T));
            }
            public T Method2()
            {
                return default(T);
            }
        }
    
    //2-定义一个接口IBar支持对参数T的协变
        interface IBar<out T>
        {
            T Method();
        }
        //实现接口IBar
        public class BarClass<T> : IBar<T>
        {
            public T Method()
            {
                return default(T);
            }
        }
    
    //3-定义一个接口IBaz支持对参数T的逆变
        interface IBaz<in T>
        {
            void Method(T param);
        }
        //实现接口IBaz
        public class BazClass<T> : IBaz<T>
        {
            public void Method(T param)
            {
                Console.WriteLine(param.ToString());
            }
        }
    //---------------应用---------------
    //1-定义两个有继承关系的类型,IParent和SubClass
        interface IParent
        {
            void DoSomething();
        }
        public class SubClass : IParent
        {
            public void DoSomething()
            {
                Console.WriteLine("SubMethod");
            }
        }
    
    //2-按照协变的逻辑,分别来使用IFoo和IBar
        //IFoo 不支持对参数T的协变
        IFoo<SubClass> foo_sub = new FooClass<SubClass>();
        IFoo<IParent> foo_parent = foo_sub;//编译错误
    
        //IBar 支持对参数T的协变
        IBar<SubClass> bar_sub = new BarClass<SubClass>();
        IBar<IParent> bar_parent = bar_sub;
    
    //3-按照逆变的逻辑,分别来使用IFoo和IBaz。
        //IFoo 对参数T逆变不相容
        IFoo<IParent> foo_parent = null;
        IFoo<SubClass> foo_sub = foo_parent;//编译错误
    
        //IBaz 对参数T逆变相容
        IBaz<IParent> baz_parent = null;
        IBaz<SubClass> baz_sub = baz_parent;
    泛型接口支持协变、逆变和不支持协变、逆变的对比

    部分说明转自:https://www.cnblogs.com/icyJ/archive/2012/11/16/covariant.html

    4. 泛型结构:非常类型于泛型类,知识没有继承特性。

    • .NET Framework的一个泛型结构是Nullable<T>
      • 主要用于将数据库中、XML数据中的可以为空的数字与.NET数字(不能为空)类型进行映射
      • 结构Nullable<T>定义了一个约束:T必须是一个结构
      • 定义了只读属性HasValue和Value,以及一些运算符重载。
      • Nullable<T> ==> T 的运算符重载是显示定义
      • T ==> Nullable<T> 的运算符重载是隐式的
    Nullable<int> x;
    x = 4;
    x += 3;
    if (x.HasValue)
    {
        int y = x.Value;
    }
    x = null;
    • 使用“?”运算符,定义可控类型的变量(int? x;)
      • 可空类型可以与null比较
      • 可空类型可以与算术运算符一起使用。若两个可空变量运算,任何一个值为null,则运算结果为null。
      • 可空类型转换为非可空类型需进行显式转换,且使用合并运算符(??)定义一个默认值,与空值映射。
    int? x1 = GetNullableType(); //GetNullableType()方法只是一个占位符,返回一个可空的int
    int y1 = x1 ?? 0;            //x1=null时赋值是默认值0

     5. 泛型方法

    • 在泛型方法中,泛型类型用方法声明来定义
    • 泛型方法可以在非泛型类中定义
    • 所调用的泛型方法是在编译期间而非运行期间定义的,因此泛型方法可以像非泛型方法那样调用
  • 相关阅读:
    看到关于java资料比较全的,自己收藏
    ie6下pnghack——css方案
    git基本操作
    购物车功能实现
    jquery学习:获取位置position(),offset(),scrollTop困惑
    Datax3.0使用说明
    Scala字符串操作
    Scala中class、object、case class、case object区别
    Scala构造函数
    scala数组操作
  • 原文地址:https://www.cnblogs.com/zhangjbravo/p/9597912.html
Copyright © 2011-2022 走看看