C#学习笔记Day6

第六章 泛型

1.优缺点

         1.性能:避免装箱拆箱操作

         例如:

        public void List()

        {

            var list = new ArrayList();

            list.Add(44);//装箱操作 Int转成引用类型

            int i = (int)list[0];//拆箱 引用类型转为Int


        }

        public void List2()

        {

            var list = new List<int>();

            list.Add(0);//没有装箱

            int i = list[0];//没有拆箱

        }

         2.类型安全

         泛型的另一个有点就是类型安全。比如上例中ArrayList中可以添加任意类型,但List<int>可以确保只能添加int型值。

         3.二进制代码重用

         4.代码的扩展

2.创建泛型类

         下面例子是一个非泛型的简化链表,他可以包含任意类型的对象。

    /// <summary>

    /// 节点类

    /// </summary>

    public class LinkedListNode

    {

        public object Value { get; private set; }

        public LinkedListNode(object value)

        {

            this.Value = value;

        }

        public LinkedListNode Next { get; internal set; }

        public LinkedListNode Prev { get; internal set; }

    }

    /// <summary>

    /// 链表

    /// </summary>

    public class LinkedList : IEnumerable

    {

        public LinkedListNode First { get;private set; }

        public LinkedListNode Last { get; private set; }

        public LinkedListNode AddLast(object node)

        {

            var newNode = new LinkedListNode(node);

            if (First==null)

            {

                First = newNode;

                Last = First;

            }

            else

            {

                LinkedListNode previous = Last;

                Last.Next = newNode;

                Last = newNode;

                Last.Prev = previous;


            }

            return newNode;

        }

        public IEnumerator GetEnumerator()

        {

            LinkedListNode current = First;

            while (current!=null)

            {

                yield return current.Value;

                current = current.Next;

            }

        }

}

现在对链表进行操作:

var list1 = new LinkedList();
list1.AddLast(2);
list1.AddLast(4);
list1.AddLast("6");
foreach (int item in list1)
{
    Console.WriteLine(item);
}

        上面操作实现了在链表中添加两个整数类型和一个字符串类型。数据类型要转换成一个对象,所以要进行装箱操作。通过foreach语句执行拆箱操作。在foreach语句中,将链表中的元素强制转换为整数,对于链表的第三个元素会发生一个运行异常。

 

        创建泛型类:泛型类的定义与一般类类似,只要使用泛型类声明。之后泛型类型就可以在类中用作一个字段成员、或者方法的参数类型。

    /// <summary>
    /// 节点类
    /// </summary>
    public class LinkedListNode<T>
    {
        public T Value { get; private set; }
        public LinkedListNode(T value)
        {
            this.Value = value;
        }
        public LinkedListNode<T> Next { get; internal set; }
        public LinkedListNode<T> Prev { get; internal set; }
    }
    /// <summary>
    /// 链表
    /// </summary>
    public class LinkedList<T> : IEnumerable<T>
    {
        public LinkedListNode<T> First { get;private set; }
        public LinkedListNode<T> Last { get; private set; }
        public LinkedListNode<T> AddLast(T node)
        {
            var newNode = new LinkedListNode<T>(node);
            if (First==null)
            {
                First = newNode;
                Last = First;
            }
            else
            {
                LinkedListNode<T> previous = Last;
                Last.Next = newNode;
                Last = newNode;
                Last.Prev = previous;

            }
            return newNode;
        }

        public IEnumerator GetEnumerator()
        {
            return GetEnumerator();
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            LinkedListNode<T> current = First;
            while (current != null)
            {
                yield return current.Value;
                current = current.Next;
            }
        }
    }

 

        使用泛型类,可以用int类型实例化他,无需装箱操作。但如果不适用AddLast()方法传递一个int,就会报错。

var list = new LinkedList<int>();
list.AddLast(1);
list.AddLast(3);
list.AddLast(4);

3.泛型类的功能

1.默认值

         介绍一个泛型文档管理器的示例。文档管理器用于从队列中读写文档。       

  public class DocumentManager<T>

    {

        private readonly Queue<T> documentQuene = new Queue<T>();

        /// <summary>

        /// 将文档添加到队列中

        /// </summary>

        /// <param name="doc"></param>

        public void AddDocument(T doc)

        {

            lock (this)

            {

                documentQuene.Enqueue(doc);

            }

        }

        /// <summary>

        /// 队列是否为空

        /// </summary>

        public bool IsDocumentAvailable

        {

            get { return documentQuene.Count > 0; }

        }

    /// <summary>

    /// 获取文档的方法

    /// </summary>

    /// <typeparam name="T"></typeparam>

        public T GetDocument()

        {

            T doc = default(T);

            lock (this)

            {

                doc = documentQuene.Dequeue();

            }

            return doc;

        }

}

        在获取文档的方法中,应该吧类型T指定为null,T doc=null。但是,不能把null赋予泛型类型。原因是泛型类型也可以实例化值类型,而null只能用于引用类型。为了解决这个问题,可以使用default关键字。用过default关键字,将null赋值给引用类型,将0赋值给值类型。

2.约束

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

  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 Content { get; set; }


        public string Title { get; set; }

}

 

    定义了接口IDocument和一个泛型类型Document,Document实现接口IDocument.

    DocumentManager<T>泛型类中添加一个实现显示所有文档Title的方法DisplayAllDocument()。要使用DocumentManager<T>类显示文档,可以将类型T强制转换为IDocument接口,以显示标题。


 

 public void DisplayAllDocument()

  {

       foreach (T doc in documentQuene)

       {

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

        }

  }

        问题是,如果T类型没有实现IDocument接口,这个强制转换就运行一个异常。最好给DocumentManager<T>定义一个约束,T必须实现接口IDocument.where自居指定实现IDocument接口的要求。

    public class DocumentManager<T>

        where T:IDocument

    {

    }

    这样就可以编写foreach语句了。

 public void DisplayAllDocument()

 {

     foreach (T doc in documentQuene)

     {

         Console.WriteLine(doc.Title);

     }

 }

    在main()方法中,用Document泛型类型初始化DocumentManager<T>类。
   

var dm = new DocumentManager<Document>();

dm.AddDocument(new Document("Title 1","Content 1"));

dm.AddDocument(new Document("Title 2", "Content 2"));

dm.DisplayAllDocument();

 

泛型接受几种约束类型:

约    束

                说   明

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.

3.继承

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

         public class LinkedList<T> : IEnumerable<T> { }

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

        public class Base<T> {}

        public class Deriver<T> :Base<T>

        其要求是必须重复接口的泛型类型,或者必须指定基类的类型,如下:

        Public class Base<T> { }

        Public class Derived<T>: Base<String> { }

4.静态成员

         泛型类的静态成员需要特变关注。泛型类的静态成员只能在一个实例中共享!(不同类型的泛型类型之间不共享静态变量)

例如:

public class StaticDemo<T>

{
     public static int x;

 }

StaticDemo<string>.x = 4;
StaticDemo<int>.x = 5;
Console.WriteLine(StaticDemo<int>.x);

 

输出结果为5

StaticDemo<string>.x = 4;

StaticDemo<int>.x = 5;

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

输出结果为4

5.泛型接口

猜你喜欢

转载自blog.csdn.net/weixin_39504659/article/details/84322423