感觉对于编程这个工作,我开始一直是这样认为的,还是我们现在好。比如说语言发展了,发展成更容易理解和编程了;工具也发展了,编程的时候更方便了,比如说现在的智能提示。
但是这样理解好像有点片面了,如果不理解一个技术的发展历史就很难真正理解它。C# 泛型编程是2.0就有的,可是自己的理解还不够,特此好好总结一下。
C# 泛型及机制
C#泛型演示
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Stack<int> test = new Stack<int>();
test.Push(10);
Console.WriteLine(test.GetResult());
Console.Read();
}
}
class Stack<T>
{
private T[] store;
private int size;
public Stack()
{
store = new T[10];
size = 0;
}
public void Push(T x)
{
store[size++] = x;
}
public T Pop()
{
return store[--size];
}
public string GetResult()
{
return store[--size].ToString();
}
}
}
C#泛型简介
所谓泛型,即通过参数化类型来实现在同一份代码上的操作多种数据类型。泛型编程是一种编程范式,它利用“参数化类型”将类型抽象化,从而实现更为灵活的复用。
C#泛型赋予了代码更强的类型安全,更好的复用,更高的效率,更清晰的约束
C#泛型机制简介
C#泛型能力由CLR在运行时支持,区别于C++ 的编译时模板机制,和Java的编译时“ 搽拭法” 。这使得泛型能力可以在各个支持CLR的语言之间进行无缝的互操作。
C#泛型代码在被编译为IL 代码和元数据时,采用特殊的占位符来表示泛型类型,并用专有的IL 指令支持泛型操作。而真正的泛型实例化工作以“on-demand” 的方式,发生在JIT编译时。
C#泛型编译机制
第一轮编译时,编译器只为Stack<T>类型产生“ 泛型版” 的IL 代码与元数据——并不进行泛型类型的实例化,T在中间只充当占位符
JIT编译时,当JIT编译器第一次遇到Stack<int>时,将用int替换“ 泛型版”IL代码与元数据中的T——进行泛型类型的实例化。
CLR 为所有类型参数为“ 引用类型” 的泛型类型产生同一份代码;但如果类型参数为“ 值类型” ,对每一个不同的“ 值类型” ,CLR将为其产生一份独立的代码
C#泛型的几个特点
如果实例化泛型类型的参数相同,那么JIT编译器会重复使用该类型,因此C#的动态泛型能力避免了C++ 静态模板可能导致的代码膨胀的问题。
•C#泛型类型携带有丰富的元数据,因此C#的泛型类型可以应用于强大的反射技术。
•C#的泛型采用“ 基类, 接口, 构造器, 值类型/引用类型” 的约束方式来实现对类型参数的 “ 显式约束” ,提高了类型安全的同时,也丧失了C++ 模板基于“ 签名” 的隐式约束所具有的高灵活性。
泛型类型
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> { };
C#除可单独声明泛型类型(包括类与结构)外,也可在基类中包含泛型类型的声明。但基类如果是泛型类,它的类型参数要么已实例化,要么来源于子类(同样是泛型类型)声明的类型参数。
泛型类型的成员
class D<V>
{
}
class C<V>
{
public V fi1; //声明字段
public D<V> f2; //作为其它泛型类型的参数
public C(V x)
{
this.fi1 = x;
}
}
泛型类型的成员可以使用泛型类型声明中的类型参数。但类型参数如果没有任何约束,则只能在该类型上使用从System.Object继承的公有成员。
泛型接口
interface IList<T>
{
T[] GetElements();
}
interface IDictionary<K, V>
{
void Add(K key, V value);
}
class List<T> : IList<T>,IDictionary<int,T>
{
public void Add(int key, T value)
{
throw new NotImplementedException();
}
public T[] GetElements()
{
throw new NotImplementedException();
}
}
泛型接口的类型参数要么已实例化, 要么来源于实现类声明的类型参数。
泛型委托
delegate bool Predicate<T>(T value);
class X
{
static bool F(int i){ return true;}
static bool G(string s) { return false; }
static void Main()
{
Predicate<string> P2 = G;
Predicate<int> p1 = new Predicate<int>(F);
}
}
泛型委托支持在委托返回值和参数上应用参数类型,这些参数类型同样可以附带合法的约束。
泛型方法
泛型方法简介
•C#泛型机制只支持 “在方法声明上包含类型参数”——即泛型方法
•C#泛型机制不支持在除方法外的其他成员(包括属性、事件、索引器、构造器、析构器)的声明上包含类型参数,但这些成员本身可以包含在泛型类型中,并使用泛型类型的类型参数
• 泛型方法既可以包含在泛型类型中,也可以包含在非泛型类型中
泛型方法的声明与调用
public class Finder
{
//泛型方法的声明
public static int Find<T>(T[] items, T item)
{
for (int i = 0; i < items.Length; i++)
{
if (items[i].Equals(item))
{
return i;
}
}
return -1;
}
//泛型方法的调用
int i = Finder.Find<int>(new int[] { 1,3,4,5,6}, 6);
}
泛型方法的重载
public 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;
}
class A{}
class B { }
泛型方法的重写
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)
{
throw new NotImplementedException();
}
// 非法的重写,指定任何约束都是多余的
public override T G<T>(T t) where T:IComparable
{
throw new NotImplementedException();
}
}
泛型约束
泛型约束简介
•C#泛型要求对“ 所有泛型类型或泛型方法的类型参数” 的任何假定,都要基于“ 显式的约束” ,以维护C#所要求的类型安全。
•“显式约束” 由where 子句表达,可以指定“ 基类约束” ,“ 接口约束” ,“ 构造器约束” ,“ 值类型/引用类型约束” 共四种约束。
•“显式约束” 并非必须,如果没有指定“ 显式约束” ,泛型类型参数将只能访问System.Object类型中的公有方法。
基类约束
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
}
接口约束
//接口约束
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的变量上调用Print和GetKey
}
构造器约束
class A{public A(){}}
class B{public B(int i){}}
class W<T> where T:new()
{
}
public class test
{
W<A> c=new W<A>(); //可以A有无参构造函数
W<B> c=new W<B>(); //错误,B没有无参构造器
}
值类型/引用类型约束