泛型编程

目录

 

一、C#泛型及机制... 2

1、通过代码初识泛型... 2

2、泛型的定义... 2

3、泛型的优势... 2

4、泛型的编译机制... 3

5C#泛型的几个特点... 3

二、泛型类型... 3

1C#泛型类与结构... 3

2、泛型类型的成员... 4

3、泛型接口... 4

4、泛型委托... 5

三、泛型方法... 5

1、泛型方法简介... 5

2、泛型方法的声明与调用... 6

3、泛型方法的重载... 6

4、泛型方法的重写... 6

四、泛型约束... 7

1、泛型约束简介... 7

2、基类约束... 7

3、接口约束... 7

4、构造器约束... 8

5、值类型/引用类型约束... 8

五、补充... 8

1、重载方法... 8

2、编译浅见... 8

3、类型参数T能不能使用属性?... 9

4C#所支持的泛型类型... 9

 

  

 

 正文

 

一、C#泛型及机制

1、通过代码初识泛型

class Stack<T>

{
private T[] store;

Private int size;

     ……

}

上面代码,蓝色部分表示泛型类型T表示类型参数T在泛型类中可以当做一个类型来使用(如用作方法的参数、返回值,字段的类型等)。

 

2、泛型的定义

所谓泛型,即通过参数化类型来实现在同一份代码上操作多种数据类型。泛型编程是一种编程范式,它利用“参数化类型”将类型抽象化,从而实现更为灵活的复用。

 

例说泛型的定义

 

某个系统需要实现一个方法。该方法需要对intbyte两种类型的参数进行处理,这就需要重载方法,即写两个方法。但这两个方法除了参数类型不同之外,基本上是一致的。代码存在重复。

使用泛型可以将类型参数设置为intbyte,因此,只需要写一个泛型方法即可实现上面所说的功能。(通过使用泛型可以更好的复用代码。)

 

3、泛型的优势

C#泛型赋予了代码更强的类型安全更好的复用更高的效率(性能)

 

例说泛型的优势

现有定义:ArrayList arrayList = new ArrayList(); 可以向arrayList中添加任何对象。如果向其中添加int类型的变量,那么在Add时需要将int类型的变量隐式装箱成引用类型。添加后要从arrayList取出数据,需要进行强制类型转换,将引用类型转换成int类型,这就有一个拆箱的过程。

 

当使用泛型时,需要指定可以向arrayList中添加数据的类型(比如int),在从arrayList取出数据时,类型也是固定的(int)。这就省去了装箱和拆箱的步骤。因此,泛型提供了更高的效率(性能)

 

还是上面的实例,如果在向arrayList添加对象时,添加了一个string类型,那么在取出数据时,强制类型转换就会出错,而这种错误在编译时是无法发现的。使用泛型则要求类型必须一致(int),这就省去了强制类型转换的步骤,不会因为强制类型转换而产生错误。因此,泛型提供了更强的类型安全

 

泛型还提供了更好的复用,参见泛型

4、泛型的编译机制

第一轮编译时,编译器只为Stack<T>类型产生“泛型版”的IL代码与元数据----并不进行泛型类型的实例化T在中间只充当占位符

 

JIT编译时,当JIT编译器第一次遇到Stack<int>时,将用int替换“泛型版”IL代码与元数据中的T----进行泛型类型的实例化

 

CLR为所有类型参数为“引用类型”的泛型类型产生同一份代码;但如果类型参数为“值类型”,对每一个不同的“值类型”,CLR将为其产生一份独立的代码。

 

关于编译过程请参考编译浅见

C#的泛型能力由CLR在运行时支持。

5C#泛型的几个特点

1、如果实例化泛型类型的参数相同,那么JIT编译器会重复使用该类型。

2C#泛型类型携带有丰富的元数据,因此C#的泛型类型可以应用于强大的反射技术。

3C#的泛型采用“基类、接口、构造器、值类型/引用类型”的约束方式来实现对类型参数的“显示约束”。 详见泛型约束

 

二、泛型类型

1C#泛型类与结构

C#除可单独声明泛型类型(包括类与结构)外,也可在基类中包含泛型类型的声明。但基类如果是泛型类,它的类型参数要么已实例化,要么来源于子类(同样是泛型类型)声明的类型参数。

 

class C<U, V> { }

//合法(单独声明的泛型类,没有继承)

 

class D : C<string, int> { }

//合法(基类是泛型类,它的类型参数已经实例化)

 

class E<U, V> : C<U, V> { }

//合法(基类是泛型类,它的类型参数来源于子类<同样是泛型类型>声明的类型参数)

 

class F<U, V> : C<string, int> { }

//合法(基类是泛型类,它的类型参数已经实例化)

 

class G : C<U, V> { }

//非法(基类是泛型类,它的类型参数应该来源于子类声明的类型参数,但子类不是泛型类)

 

2、泛型类型的成员

泛型类型的成员可以使用泛型类型声明中的类型参数。但类型参数如果没有约束,则只能在该类上使用从System.Object继承的共有成员。详见泛型约束

 

class C<V>

{

    public V f1;//声明字段

    public D<V> f2; //作为其他泛型类型的参数

   public C(V x)

   {

        this.f1 = x;

    }

}

3、泛型接口

泛型接口的类型参数要么已实例化,要么来源于实现类声明的类型参数。

 

interface IList<T>

{

    T[] GetElements();

}

interface IDictionary<K, V>

{

    void Add(K key, V value);

}

class List<T> : IList<T>, IDictionary<int, T>

{

    public T[] GetElements()

    {

        return null;

    }

    public void Add(int key, T value)

    {

       

    }

}

 

4、泛型委托

泛型委托支持在委托返回值和参数上应用类型参数,这些类型参数同样可以附带合法的约束。详见泛型约束

 

delegate bool Predicate<T>(T value);

class X

{

    static bool F(int i) { return false; }

    static bool G(string s) { return true; }

    static void Main()

    {

        Predicate<int> p1 = new Predicate<int>(F);

        Predicate<string> p2 = G;

    }

}

三、泛型方法

1、泛型方法简介

C#泛型机制只支持“在方法声明上包含类型参数”----即泛型方法。

 

C#泛型机制不支持在除方法外的其他成员(包括属性、事件、索引器、构造器、析构器)的声明上包含类型参数,但这些成员本身可以包含在泛型类型中,并使用泛型类型的类型参数。

 

泛型方法既可以包含在泛型类型中,也可以包含在非泛型类型中。

 

2、泛型方法的声明与调用

public class Finder

{

    //泛型方法的声明

    public static int Find<T>(T[] itmes, T item) { return -1; }

}

//泛型方法的调用

int i = Finder.Find<int>(new int[] {1,3,4,5,6,8,9 }, 6);

 

3、泛型方法的重载

   class MyClass

    {

        void F1<T>(T[] a, int i);  //不可以构成重载方法

        void F1<U>(U[] a, int i);

 

        void F2<T>(int x);  //可以构成重载方法

        void F2(int x);

 

        void F3<T>(T t) where T : A;  //不可以构成重载方法

        void F3<T>(T t) where T : B;

}

 

重载方法的含义详见重载

 

4、泛型方法的重写

abstract class Base

{

    public abstract T F<T, U>(T t, U u) where U : T;

    public abstract T G<T>(T t) where T : IComparable;

 

    class Derived : Base

    {

        public override T F<T, U>(T t, U u) { }

        //合法的重写,约束被默认继承

 

        public override T G<T>(T t) where T:IComparable { }

        //非法的重写,指定任何约束都是多余的

    }

}

四、泛型约束

1、泛型约束简介

C#泛型要求对“所有泛型类型或泛型方法的类型参数”的任何假定,都要基于“显示的约束”,以维护C#所要求的类型安全。

 

“显示约束”由where子句表达,可以指定“基类约束”,“接口约束”,“构造器约束”,“值类型/引用类型约束”共四种约束。

 

“显示约束”并非必须,如果没有指定“显示约束”,泛型类型参数将只能访问System.Object类型中的公有方法。

2、基类约束

class A { public void F1() { } }

class B { public void F2() { } }

class C<S, T>

    where S : A  //S继承自A

    where T : B  //T继承自B

{

    //可以在类型为S的变量上调用F1

    //可以在类型为T的变量上调用F2

}

3、接口约束

interface IPrintable { void Print();}

interface IComparable<T> { int CompareTo(T v);}

interface IKeyProvider<T> { T GetKey();}

 

class Dictionary<K, V>

    where K : IComparable<K>

    where V : IPrintable, IKeyProvider<K>

{

    //可以在类型为K的变量上调用CompareTo

    //可以在类型为V的变量上调用PrintGetKey

}

4、构造器约束

   class A { public A() { } }

    class B { public B(int b) { } }

    class C<T> where T : new()

    {

        //可以在其中使用T t = new T();

}

 

    C<A> c = new C<A>();//可以,A有无参构造器

C<B> c = new C<B>();//错误,B没有无参构造器

 

目前C#泛型只支持无参构造器的约束。

5、值类型/引用类型约束

   public struct A { }

    public class B { }

    class C<T>

        where T : struct

    {

        //T在这里是一个值类型

}

 

    C<A> c = new C<A>(); //可以,A是一个值类型

    C<B> c = new C<B>(); //错误,B是一个引用类型

五、补充

1、重载方法

方法的签名一致。即方法名称一致,参数列表(类型或个数)不一致。

2、编译浅见

第一步,将源代码编译成IL和元数据。

第二步,CLR在运行时的JIT编译。将IL代码编译成机器代码。

JIT采用ON-DEMAND的方式进行实例化。用到才实例化,不用就不实例化。

3、类型参数T能不能使用属性?

C#泛型目前不支持参数类型T使用属性。可以使用如下办法来实现。创建一个类C,使C包含T要使用的所有属性。再通过基类约束,显示约束T继承C。即可使用C中定义的属性。

4C#所支持的泛型类型

C#的泛型支持包括类、结构、接口、委托共四种泛型类型,以及方法成员。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值