《C#高级编程》【第五章】泛型 -- 学习笔记

来源:互联网 发布:无印良品水乳 知乎 编辑:程序博客网 时间:2024/06/13 17:41


       泛型是高级程序设计语言的一种特性。泛型的引入使得程序开发的效率得到提高,代码的重用性大大的提升。有了泛型,我们可以创建独立于被包含类型的类和方法,我们不必给不同的类编写功能相同的很多方法或者类,只创建一个方法或类就可以了。现在我们看看泛型的优点

       性能上,泛型不需要进行类型转换(也就是拆箱和装箱)。
       类型安全,和Object类相比,Object类属于非类型安全的,而泛型使用泛型类型,可以根据需要用特定的类型替换泛型类型,这样就保证了类型安全类。在编译时,只要与泛型类型T定义的允许使用的类型不同,编译器就会报错,这样我们就可以尽早的发现错误。
       二进制代码重用,在C#中可以一次定义多次使用,然而C++却要不断的访问源码。
       代码的扩展,对于引用类型,他们将会共享本地类的所有相同代码。只有值类型,才会每次实例化一个新类。
       我们看完了泛型的优点,我们现在可以来看看泛型具体怎么使用。我们先看一幅图,了解一下泛型的组成。

1、泛型类
    我们先看看泛型类型的命名方法:<1>一大写字母T开头 <2>如果泛型类型没有特定的要求,或者使用了两个、两个以上的泛型类型,就需要给出描述性的名称。(如:TValue,TKey等等)。
现在我们看看泛型类的声明语法:

[访问权限修饰符] class 类名<T>{//类体}

       类名的T就是泛型类型,具体类型实例化时给出。然而在类体中就可以直接将T当成一个具体的类型来使用。每个处理对象类型的类都可以有泛型实现方式。如果类使用了层次结构,那么泛型就可以很好的消除类型转换的操作。
注意:T两边的’<’和’>’不能少。
       在创建泛型类时,我们还需要考虑一些问题。首先我们应该怎么初始化成员变量呢?然而我们并不知道究竟是引用类型还是值类型,为了解决这个问题我们就引入了default关键字。语法如下:

T value = default(T)

     这样我们就圆满的解决成员变量初始化的问题。default会给值类型赋值为0,引用类型赋值为null。
     如果泛型类需要调用泛型类型的方法,那么就必须添加约束。那么什么是约束呢?约束就是让泛型类型只能使用我们所约束的类型。我们使用where关键字来声明约束。公有以下6种约束:
<1>where T : struct 结构约束,类型T必须为值类型
<2>where T : class 引用约束,类型T必须是引用类型
<3>where T : IFoo 接口约束,类型T必须实现接口IFoo
<4>where T : Foo 类约束,类型T必须派生自基类Foo
<5>where T : new() 类型T必须有一个默认的构造函数
<6>where T1 : T2 裸型约束,类型T1派生自泛型T2。
对于裸型约束,我们来具体说说吧。我们举个例子:

public class Test<T1, T2> where T1 : T2{//类体}

       我们重点看看实例化的时候

var MyTest = new Test<Class1, Class2>();  

       那么此时,Class1类就必须派生自Class2类,否则编译器就会报错。
       使用泛型类型还可以组合多个约束,例如:

public class MyClass<T> where T : IFoo, new(){//类体}

       泛型类,既然作为类那么它必然可以继承。泛型类可以实现泛型接口,也可以派生自泛型基类。例:

public class Myclass<T> : IDD<T>{//类体}
或者这样

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

也可以是这样:

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

派生类可以是泛型类还可以是非泛型类。

public classMysub: Base<int>{}

       泛型类,比较特别的应该就是它的静态成员了。因为泛型类的静态成员只能在类的一个实例中共享。假设类MySub<T>存在静态字段x,那么如果同时对一个int型和string型使用了Mysub<T>类,所以就存在两组字段:Mysub<int>.x和MySub<string>.x
2、泛型接口
        看到泛型接口,我们就要提到,抗变与协变。现在我们就看看什么抗变与协变。
        假设:TSub是TParent的子类。
        协变:如果一个泛型接口IFoo<T>,IFoo<TSub>可以转换为IFoo<TParent>的话,我们称这个过程为协变,IFoo支持对参数T的协变。
        逆变:如果一个泛型接口IFoo<T>,IFoo<TParent>可以转换为IFoo<TSub>的话,我们称这个过程为逆变,IFoo支持对参数T的逆变。
        泛型类型的协变用关键字out声明,泛型接口就是协变的,这就意味着其返回类型只能是T。

public interface IIndex<out T>{T this[int index] {get ;}}

泛型类型的抗变用关键字in声明,泛型接口就是抗变的。这样泛型类型T就只能作为方法输入。例如:

public interface IDisplay<in T>{void Show(T test);}

       通常只有具备继承关系的对象才可以发生隐式类型转换,如Base b=new sub()。
       协变和逆变可以使得更多的类型之间能够实现隐式类型转换、类型安全性得到保障。
3、泛型结构
       其实泛型结构和泛型类几乎是一致的,只是泛型结构没有继承的特性。.Net平台提供的一个泛型结构是(可空类型)Nullable<T>。可空类型的引入,主要是为了解决数据库语言中的数字与编程语言中的数字的区别(数据库中数字可以为空,编程语言中数字不可为空)。因为Nullable<T>使用过于的繁琐,于是我们就引入了一种特殊的语法,使用‘?’运算符。例:

int? x1;Nullable<int> x2;
在上面的例子中,x1与x2这两种方式定义是等价的。
非空类型可以转化为可空类型。(总是成功的且可以隐式转化)
可空类型可以转化为非空类型。当可空类型的值为null时就会抛出异常。(需要显示转化)
如果不进行显示转化,我们就可以使用”??”运算符。如下:
int? x1 = GetNullableType();int y1 = x1 ?? 0;

这样的话,当x1为null时,就会赋给y1一个0。
4、泛型方法
      除了可以定义泛型类,泛型也是可以定义成方法的。语法如下:
[返回值类型] 方法名称<T>(参数列表){}
      其余的用法就和普通方法是一样的,唯一的区别就是它有一个通用类型T。
      泛型方法和类一样,也可以加约束,具体约束的方法和泛型类是一样的。语法如下:
[返回值类型] 方法名称<T>(参数列表) where T : [约束方式]{}

以上就是泛型的内容了

2 0
原创粉丝点击