• C#高级编程笔记 Day 6, 2016年9月 14日 (泛型)


    1、泛型类的功能:在创建泛型类时,还需要一些其他C#关键字。例如,不能把null赋予泛型类型。此时,可以使用default 关键字。如果泛型类型不需要Object类的功能,但需要调用泛型类上的某特定方法,就可以定义约束。

     具体讨论以下:

    • 默认值
    • 约束
    • 继承
    • 静态成员

    例:下面是一个使用 泛型文档管理器示例。DocumentManager

    •  1 using System;
       2 using System.Collections.Generic;
       3 namespace Jandar.Test.YinXi{
       4     public class DocumentManager<T>{
       5         private readonly Queue<T> documentQueue=new Queue<T>();
       6         public void AddDocument(T doc){
       7             lock(this){
       8                 documentQueue.Enqueue(doc);
       9             }
      10         }
      11         public bool IsDocumentAvailable{
      12             get{
      13                 return documentQueue.Count>0;
      14             }
      15         }
      16 }

    除了IEnumerable 接口,还有一个泛型版本 IEnumerable<T>。IEnumerable<T> 派生自IEnumerable 添加了IEnumerator<T> 的GetEnumerator() 方法,LinkeedList<T>实现泛型接口 IEnumerable<T>

      【默认值】

        给DocumentManager<T>类添加一个 GetDocument() 方法。在这个方法中,应把 类型T 指定为null。但是,不能把null 赋予泛型类型。原因是泛型类型也可以实例化为值类型,而null 只能用于引用类型。为了解决这个问题,可以使用 default 关键字。通过default 关键字,将null 赋予引用类型,将 0 赋予值类型。

    •   
      1 public T GetDocument()
      2 {
      3      T doc =default(T);
      4     lock(this)
      5     {
      6         doc=documentQueue.Dequeue();
      7      }
      8     return doc;
      9 }

      default 关键字根据上下文可以有多种含义。 switch 语句使用 default 定义默认情况。在泛型中,根据泛型类型是引用类型还是值类型,泛型default 用于将泛型类型初始化为 null 或 0;

      【约束】

    • Document 类实现带有 Title 和Content属性的 IDocument 接口。
       1 public interface IDocument
       2 {
       3     string Title {get; set;}
       4     string Content{get; set;}
       5  }
       6 
       7 public  class Document: IDocument
       8 {
       9     public Document()
      10     {
      11 
      12      }
      13     public Document(string title,string content)
      14     {
      15         this.Title=title;
      16         this.Content=content;
      17     }
      18     public string Title{ get; set;}
      19     public string Content{getl;set;}
      20 }
    • 要使用DocumentManager<T> 类显示文档,可以将类型 T 强制转换为 IDocument 接口,以显示标题 title 或者 内容 content
    • 1 public void DisplayAllDocuments()
      2 {
      3     foreach(T doc in documentQueue)
      4     {
      5         Console.WriteLine(((IDocument)doc).Title);
      6     }
      7 }
    • 问题是如果类型T没有实现IDocument 接口,这个类型强制转换就会导致一个运行异常。最好给DocumentManager<TDocument>类定义一个约束: TDocument 类型必须实现 IDocument 接口,
    • 1 public class DocumentManager<TDocument> where TDocument : IDocument
      2 {
      3      ...
      4 }
      约束 说明

      where T : struct

      对于结构约束,类型 T 必须是值类型

      where T : class

      类约束指定类型 T 必须是引用类型

      where T : IFoo

      指定类型 T 必须实现接口 IFoo

      where T : Foo

      指定类型 T 必须派生自基类 Foo

      where T : new()

      这是一个构造函数约束,指定类型 T 必须有一个默认构造函数

      where T1 : T2

      这个约束也可以指定,类型T1 派生自 泛型类型T2。 该约束也成为裸类型约束

      只能为默认构造函数定义构造函数约束,不能为其他构造函数定义构造函数约束

        使用泛型类型还可以合并多个约束。 where T :IFoo,new() 约束和 MyClass<T>声明指定,类型T 必须实现IFoo 接口,且必须有一个默认构造函数。

      •       
        public class MyClass<T> where T :IFoo, new()
        {
            ...
        }

       【继承】

       可以定义一个抽象的泛型基类,它在派生类中用一个具体的类型实现。

      •       
         1 public abstract class Calc<T>
         2 {
         3     public abstract T Add(T x, T y);
         4     public abstract T Sub(T x, T y);
         5 }
         6 
         7 public class IntCalc :Calc<int>
         8 {
         9     public override int Add(int x ,int y)
        10     {
        11         return x+y;
        12      }
        13     public override int Sub(int x, int y)
        14     {
        15         return x-y;
        16     }
        17 }

        【静态成员】

        泛型类的静态成员需要特别关注。泛型类的静态成员只能在类的一个实例中共享。

    •     
      1 public class StaticDemo<T>
      2 {
      3     public static int x;
      4 }
      5 
      6 //由于同时对一个string类型和一个int 类型使用了 StaticDemo<T> 类。所以存在两组静态字段。
      7 StaticDemo<string>.x=4;
      8 StaticDemo<int>.x=5;
      9 Console.WriteLine(StaticDemo<string>.x);//4

    2、泛型接口

       【协变和抗变】

        协变和抗变指对参数和返回值类型进行转换。

        在.NET 中,参数类型是协变的。假定有 Shape 和 Rectangle 类,Rectangle 派生自Shape基类。声明 Display()方法是为了接受Shape 类型的对象作为其参数:    

    public void Display(Shape o){ }

        方法的返回类型是抗变的。当方法返回一个Shape 时,不能把它赋予 Rectangle, 因为Shape不一定总是Rectangle。反过来是可行的:如果一个方法像 GetRectangle() 方法那样返回一个Rectangle,

    public Rectangle GetRectangle();

    就可以把结果赋予某个Shape

    Shape s=GetRectangle();

    在 .NET Framework4 版本之前,这种行为方式不适用于泛型。 在C# 4 中,扩展后的语言支持泛型接口和泛型委托的协变和抗变。

      泛型接口的协变

      如果泛型类型用 out 关键字标注,泛型接口就是协变的。这也意味着返回类型只能是 T 。 接口 IIndex 与 类型 T 是协变的,并从一个只读索引器中返回这个类型。

    public interface IIndex<out T>
    {
        T this[int index] { get;}
        int Count{get;}
    }
  • 相关阅读:
    vue 拖拽移动(类似于iPhone虚拟home )
    鼠标事件-MouseEvent【转】
    JS快速排序 希尔排序 归并排序 选择排序
    JS 继承
    Centos6 iptables 防火墙设置【转】
    centos6 mongodb 安装
    操作系统中涉及的各种调度算法
    循环队列
    队列
    栈(C++)
  • 原文地址:https://www.cnblogs.com/xiyin/p/5872513.html
Copyright © 2020-2023  润新知