C#中的泛型

参考书:《c#2.0完全自学手册》  《C# 2005 & .NET 3.0高级编程(第5版)

 

NET 2.0提供了泛型。有了泛型,就不再需要Object类了

泛型类使用泛型类型,并可以根据需要用特定的类型替换泛型类型

 

1.概述

型并不是一个全新的结构,其他语言中有类似的概念

如:C++模板就与泛型相当

泛型不仅是C#语言的一种结构,而且是CLR定义的

所以,即使泛型类是在C#中定义的,也可以在Visual Basic中用一个特定的类型实例化该泛型

 

2.泛型的优点

(1)性能

System.Collections —— 非泛型集合类

System.Collections. Generic ——  泛型集合类

 

对值类型使用非泛型集合类,在把值类型转换为引用类型,和把引用类型转换为值类型时,需要进行装箱和拆箱操作

 

[知识点]装箱和拆箱

.NET很容易把值类型转换为引用类型,所以可以在需要对象(对象是引用类型)的任意地方使用值类型

例如,int可以赋予一个对象

从值类型转换为引用类型称为装箱。如果方法需要把一个对象作为参数,而且传送了一个值类型,装箱操作就会自动进行

 

另一方面,装箱的值类型可以使用拆箱操作转换为值类型。在拆箱时,需要使用类型转换运算符

 

装箱和拆箱操作很容易使用,但性能损失比较大,迭代许多项时尤其如此

 

 

而通过使用泛型,如:

System.Collections.Generic命名空间中的List<T>类不使用对象,而是在使用时定义类型

List<int> list = new List<int>();

 

 

List<T>类的泛型类型定义为int,所以int类型在JIT编译器动态生成的类中使用,不再进行装箱和拆箱操作

 

(2)类型安全

(3)二进制代码的重用

更好地重用二进制代码。泛型类可以定义一次,用许多不同的类型实例化。而不需要像C++模板那样访问源代码

泛型类型可以在一种语言中定义,在另一种.NET语言中使用

(4)泛型类的定义会放在程序集中,所以用某个类型实例化泛型类不会在IL代码中复制这些类

但在JIT编译器把泛型类编译为内部码时,会给每个值类型创建一个新类;引用类型共享同一个内部类的所有实现代码

(5)命名约定

泛型类型的名称用字母T作为前缀

如没有特殊的要求,泛型类型允许用任意类替代,且只使用了一个泛型类型,就可以用字符T作为泛型类型的名称

如泛型类型有特定的要求(比如:必须实现一个接口或派生于基类),或者使用了两个或多个泛型类型,那么就应给泛型类型使用描述性的名称

 

[2009-3-15](续)

2.创建泛型类(通过代码及注释来说明)

实现一个简单链表

//定义了一个泛型类MyList
public class MyList<T>    //参数T,即类型(类型参数),可以通过传入不同的类型,使得整个类中T都替换为这个类型
{
        private MyItem myItem;  //定义MyItem对象
       

        public MyList()
        {
            myItem = null;
        }

 

        /// <summary>
        /// 向MyList中添加元素(实际是MyItem对象)
        /// </summary>
        /// <param name="t"></param>
        public void Add(T t)
        {
            MyItem n = new MyItem(t);
            n.Next = myItem;
            myItem = n;
        }

 

        //使用迭代器
        public IEnumerator<T> GetEnumerator()
        {
            MyItem currentItem = myItem;
            while (currentItem != null)
            {
                yield return currentItem.Data;     //迭代器依次返回每个元素
                currentItem = currentItem.Next;  //下一个
            }
        }

 

 

        //表示MyList中每个元素
        private class MyItem     //MyItem分为两部分:存储MyList元素的data变量,指向下一个元素的next变量
        {
            private MyItem next;
            private T data;
            public MyItem(T t)
            {
                next = null;
                data = t;
            }

 

            public MyItem Next
            {
                get
                {
                    return next;
                }
                set
                {
                    next = value;
                }
            }


            public T Data
            {
                get
                {
                    return data;
                }
                set
                {
                    data = value;
                }
            }

        }
}


//主程序中调用
MyList<int> myList = new MyList<int>();
for (int x = 0; x < 5; x++)
{
 myList.Add(x);
}

foreach (int i in myList)  //输出
{
 System.Console.WriteLine(i);
}

 

 

 3.泛型类的特性

(1)默认值:

当需要给类型T指定null

而不能把null赋予泛型类型,原因是泛型类型也可以实例化为值类型,而null只能用于引用类型

 

此时可以使用default关键字

通过default关键字,将null赋予引用类型,将0赋予值类型

 

如:方法中给类型T指定null

T xxx= default(T);

 

注意:在泛型中,根据泛型类型是引用类型还是值类型,default关键字用于将泛型类型初始化为null0

 

(2)约束

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

public class DocumentManager<TDocument> where TDocument : IDocument

...................

DocumentManager<TDocument>类定义一个约束:

TDocument类型必须执行IDocument接口

where子句指定了执行IDocument接口的要求

泛型还有几种约束类型(如下表)

       约   

                          说        明

where T : struct

使用结构约束,类型T必须是值类型

where T : class

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

where T : IFoo

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

where T : Foo

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

where T : new()

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

where T : U

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

 

注意:CLR 2.0中,只能为默认构造函数定义约束,不能为其他构造函数定义约束

使用泛型类型还可以合并多个约束

where T : IFoo,new()约束和MyClass<T>声明指定,类型T必须执行IFoo接口,且必须有一个默认构造函数

(3)继承

泛型类型可以执行泛型接口,也可以派生于一个类。

泛型类可以派生于泛型基类,要求是必须重复接口的泛型类型,或者必须指定基类的类型

如:

//基类

public class Base<T>

{

}

 

public class Derived<T> : 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 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;

}

}

 

 

(4)静态成员

泛型类的静态成员只能在类的一个实例中共享

public class StaticDemo<T>

{

        public static int x;         //StaticDemo<T>类包含静态字段x

}

 

StaticDemo<string>.x = 4;      //string类型使用了StaticDemo<T>类

StaticDemo<int>.x = 5;          //int类型使用了StaticDemo<T>类

 

//以上是存在两组静态字段 

 

Console.WriteLine(StaticDemo<string>.x); // 输出的结果是4

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值