[读书笔记] 泛型编程特性

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

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

1.默认值

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Generics
{
    public class DocumentManager<T>
    {
        private readonly Queue<T> documentQueue = new Queue<T>();

        public void AddDocument(T doc)
        {
            lock (this)
            {
                documentQueue.Enqueue(doc);
            }
        }

        public bool IsDocumentAvailable
        {
            get
            {
                return documentQueue.Count > 0;
            }
        }

        // 默认值
        // 现在给DocumentManager<T>类添加一个GetDocument()方法
        // 给类型T指定null,但是不能把null赋值给泛型类型
        // 泛型类型也可以实例化为值类型,但是null只能用于引用类型,解决这个问题可以使用default关键字
        // default关键字,将null赋予引用类型,将0赋予值类型       
        public T GetDocument()
        {
            T doc = default(T);
            lock (this)
            {
                doc = documentQueue.Dequeue();
            }
            return doc;
        }
    }

    // 约束
    // 如果泛型类需要调用泛型类型上的方法,就必须添加约束
    public interface IDocument
    {
        string Title
        {
            get;
            set;
        }
        string Content
        {
            get;
            set;
        }
    }

    public class Document : IDocument
    {
        public string Title
        {
            get;
            set;
        }

        public string Content
        {
            get;
            set;
        }

        public Document()
        { 
        }

        public Document(string title, string content)
        {
            this.Title= title;
            this.Content = content;
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

2.约束

如果泛型类需要调用泛型类型上的方法,就必须添加约束。

对于DocumentManager<T>,文档的标题应在DisplayAllDocument()方法中显示。

        // 要使用DocumentManger<T>来显示文档,可以将类型T强制转换为IDocument接口
        public void DisplayAllDocuments()
        {
            foreach (T doc in documentQueue)
            {
                Console.WriteLine(((IDocument)doc).Title);
            }
        }

如果类型T没有执行IDocument接口,这个类型转换就会生成一个运行异常。最好的办法就是给DocumentManger<TDocument>类定义一个约束:TDocument类型必须执行IDocument接口。

public class DocumentManager<TDocument> where TDcoument : IDocument

这样上面的语句就可以这样写了

        // 要使用DocumentManger<T>来显示文档,可以将类型T强制转换为IDocument接口
        public void DisplayAllDocuments()
        {
            foreach (TDocument doc in documentQueue)
            {
                Console.WriteLine(doc.Title);
            }
        }

下面是加了约束和带有默认值特性的DocumentManager泛型类的完整代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Generics
{
    public class DocumentManager<TDocument> where TDocument : IDocument
    {
        private readonly Queue<TDocument> documentQueue = new Queue<TDocument>();

        public void AddDocument(TDocument doc)
        {
            lock (this)
            {
                documentQueue.Enqueue(doc);
            }
        }

        public bool IsDocumentAvailable
        {
            get
            {
                return documentQueue.Count > 0;
            }
        }

        // 默认值
        // 现在给DocumentManager<T>类添加一个GetDocument()方法
        // 给类型T指定null,但是不能把null赋值给泛型类型
        // 泛型类型也可以实例化为值类型,但是null只能用于引用类型,解决这个问题可以使用default关键字
        // default关键字,将null赋予引用类型,将0赋予值类型       
        public TDocument GetDocument()
        {
            TDocument doc = default(TDocument);
            lock (this)
            {
                doc = documentQueue.Dequeue();
            }
            return doc;
        }

        public void DisplayAllDocuments()
        {
            foreach (TDocument doc in documentQueue)
            {
                Console.WriteLine(doc.Title);
            }
        }
    }

    // 约束
    // 如果泛型类需要调用泛型类型上的方法,就必须添加约束
    public interface IDocument
    {
        string Title
        {
            get;
            set;
        }
        string Content
        {
            get;
            set;
        }
    }

    public class Document : IDocument
    {
        public string Title
        {
            get;
            set;
        }

        public string Content
        {
            get;
            set;
        }

        public Document()
        { 
        }

        public Document(string title, string content)
        {
            this.Title= title;
            this.Content = content;
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            DocumentManager<Document> dm = new DocumentManager<Document>();
            dm.AddDocument(new Document("Title A", "Sample A"));
            dm.AddDocument(new Document("Title B", "Sample B"));

            dm.DisplayAllDocuments();

            if (dm.IsDocumentAvailable)
            {
                Document d = dm.GetDocument();
                Console.WriteLine(d.Content);
            }

            Console.ReadLine();
        }
    }
}

出来的结果有点问题: 这个需要后面看下,怎么回事。为什么Document B的内容显示不了

Title A
Title B
Sample A

上面的例子是一个关于接口约束的例子。泛型还有几种约束类型

where T : struct 使用结构约束 类型T必须是值类型

where T : calss 类约束指定, 类型T必须是引用类型

where T : IFoo 指定类型必须执行接口IFoo

where T : Foo 指定类型必须派生于基类Foo

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

where T : U 这个约束也可以指定,类型T1派生于泛型类型T2。该约束也称为裸类型约束

3.继承

泛型类型可以执行泛型接口,也可以派生于一个类。下面是几种泛型接口的继承

public class Base<T>
{
}

public class Derived<T> : Base<T>
{
}

也可以这样:

public class Base<T>
{
}

public class Derived<T> : Base<string>
{
}

派生类可以是泛型类也可以是非泛型类。例如下面的计算器

public abstrac class Clac<T>
{
    public abstrac T Add(T x, T y);
    public abstrac T Sub(T x, T y);
}

public class SimpleCalc : Calc<int>
{
     public override int Add(int x, int y)
     {
         return x + y;
      }

     public override int Sub(int x, int y)
     {
         return x - y;
      }

}

这样的程序看起来,非常干净不错O(∩_∩)O~

4.静态成员

泛型类的静态成员需要特别关注。泛型类的静态成员只能在类的一个实例中共享。看下面的例子就知道了

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GenericStatic
{
    class Program
    {
        public class StaticDemo<T>
        {
            public static int x;
        }

        static void Main(string[] args)
        {
            StaticDemo<string>.x = 4;
            StaticDemo<int>.x = 5;

            Console.WriteLine(StaticDemo<string>.x);
            Console.ReadLine();
        }
    }
}

结果输出肯定很清楚了

4

原文地址:https://www.cnblogs.com/herbert/p/1742925.html