zoukankan      html  css  js  c++  java
  • C#编程(二十八)----------泛型类的功能

    泛型类的功能

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

    例如:

    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,并添加DocumentManager<T>.AddDocument()方法将一个文档添加到队列中.如果队列不为空,IsDocumentAvailable只读属性就返回true.

    默认值

    接下来给DocumentManager<T>类添加衣蛾GetDocument()方法.

     public T GetDocument()

            {

                T doc = default(T);

                lock (this)

                {

                    doc = documentQueue.Dequeue();

                }

                return doc;

            }

    在这个方法中,应该把类型T指定为null.但是不能把null赋予泛型类型.隐隐是泛型类型也可以实例化为值类型,null只能用于引用类型.为了解决这个问题,使用了default关键字,通过default关键字,null赋予引用类型,0赋予值类型.

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

    约束

    如果泛型类型需要调用泛型类型中的方法,就必须加以约束.对于DocumentManager<T>,文档的所在标题应在DisplayAllDocuments()方法显示.Document类实现带有TitleCOntent属性的IDocument接口:

    public interface IDocument

        {

            string Title { get; set; }

            string Content { get; set; }

        }

        public class Document : IDocument

        {

            public Document()

            {

            }

            public Document(string title, string content)

            {

                this.Title = title;

                this.Content = content;

            }

            public string Title { get; set; }

            public string Content { get; set; }

    }

    要使用DocumentManager<T>类显示文档,可以将类型T强制转换为IDocument接口,以显示标题:

     public void DisplayAllDocument()

            {

                foreach (T doc in documentQueue)

                {

                    Console.WriteLine(((IDocument)doc).Title);

                }

            }

    问题是,如果类型T没有实现IDocument接口,这个类型强制转换就会导致一个运行异常.最好给DocumentManager<TDocument>类定义一个约束:TDocument类型必须实现IDocument接口.为了在反省类型的名称中指定该要求,T改为TDocument.where子句指定了实现IDocument接口的要求.

    public class DocumentManager<TDocument>

    where TDocument : IDocument

    {}

    这样就可以编写foreach语句,从而使类型TDocument包含属性TItle. Visual Studio InterlliSense和编译器都会提供这个支持.

    public void DisplayAllDocument()

    {

    foreach (TDocument doc in documentQueue)

    {

    Console.WriteLine(doc.Title);

    }

    }

    main()方法中,Document类型实例化Documentmanager

    <T>,Document类型实现了需要IDocument接口.接着添加和显示新文档,检索其中一个文档:

    static void Main()

    {

    var dm= new Documentmanager<Document>();

    dm.AddDocument(new Document(“Title A”,”Sample A”));

    dm.AddDocument(new Document(“Title B”,”Sample B”));

    dm.DisplayAllDocument();

    if (dm.IsDocumentAvailable)

    {

    Document d=dm.GetDocument();

    Console.WriteLine(d.Content);

    }

    }

    DocumentManager现在可以处理任何实现了IDocument接口的类.

    在示例应用程序中,介绍了接口约束.泛型支持集中约束类型,如下表:

    约束

    说明

    where T :struct

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

    where T:class

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

    where T:IFoo

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

    whereT:Foo

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

    whereT:new()

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

    where T:T2

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

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

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

    public class MyClass<T>

    where T:IFoo,new()

    {

    //..

    }

    C#,where子句的一个重要限制是,不能定义必须由泛型类型实现的运算符.运算符不鞥在接口中定义.where子句中,只能定义基类,接口和默认构造函数.

    以上案例的完整代码:

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

    using System.Threading.Tasks;

    namespace ConsoleApplication9

    {

        class Program

        {

            static void Main(string[] args)

            {

                var dm = new DocumentManager<Document>();

                dm.AddDocument(new Document("Title A", "Sample A"));

                dm.AddDocument(new Document("Title B", "Sample B"));

                dm.DisplayAllDocument();

                if (dm.IsDocumentAvailable)

                {

                    Document d = dm.GetDocument();

                    Console.WriteLine(d.Content);

                }

                Console.ReadKey();

            }

        }

        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;

                }

            }

            public TDocument GetDocument()

            {

                TDocument doc = default(TDocument);

                lock (this)

                {

                    doc = documentQueue.Dequeue();

                }

                return doc;

            }

            public void DisplayAllDocument()

            {

                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 Document()

            {

            }

            public Document(string title, string content)

            {

                this.Title = title;

                this.Content = content;

            }

            public string Title { get; set; }

            public string Content { get; set; }

        }

    }

    继承

    前面创建的LinkedList<T>类实现了IEnumerable<out T>接口:

    public class LinkedList<T>:IEnumerable<out T>

    {

    //...

    }

    泛型类型可以实现泛型接口,也可以派生自一个类.泛型类还可以派生自泛型基类:

    public class Base<T>

    {

    }

    public class Derived<T>:Base<T>

    {}

    要求是必须重复接口的泛型类型,或者必须制定基类的类型,:

    public class Base<T>

    {

    }

    public class Derived<T>:Base<string>

    {}

    于是,派生类可以是泛型类或者非泛型类.例如,可以定义一个抽象的泛型基类,它在派生类中用一个具体的类型实现.这允许对特定类型执行特殊的操作:

    public abstract class Calc<T>

        {

            public abstract T Add(T x, T y);

            public abstract T Sub(T x, T y);

        }

        public class IntCalc : Calc<int>

        {

            public override int Add(int x, int y)

            {

                return x + y;

            }

            public override int Sub(int x, int y)

            {

                return x - y;

            }

        }

    静态成员

    泛型类的静态成员需要特别关注.泛型类的景天成员只能在类的一个实例中共享.看下例,其中StaticDemo<T>类包含静态字段x:

      public class StaticDemo<T>

        {

            public static int x;

    }

    Main()函数中的代码如下:

    static void Main(string[] args)

            {

                StaticDemo<string>.x = 4;

                StaticDemo<int>.x = 5;

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

                Console.ReadKey();

            }

    由于同时对一个string类型和一个int类型使用了StaticDemo<T>,所以存在两组静态类型.

  • 相关阅读:
    【转】数据库分页Java实现
    【转】ibatis的简介与初步搭建应用
    response.setContentType()的作用及参数
    【转】mysql数据库中实现内连接、左连接、右连接
    【转】JAVA的StringBuffer类
    【转】Java学习之Iterator(迭代器)的一般用法 (转)
    函数装饰器
    闭包函数
    函数(2)
    函数(1)
  • 原文地址:https://www.cnblogs.com/android-blogs/p/6593109.html
Copyright © 2011-2022 走看看