什么是泛型?
• 可以使用任何类型来声明和实例化。
• 声明和实例化时都必须用一个特定的类型来代替一般类型
T
Stack stack = new Stack();
stack.Push(1);
int number = (int)stack.Pop();
Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
int number = stack.Pop();
public class Stack
{
object[] m_Items;
public void Push(object item)
{...}
public object Pop()
{...}
}
public class Stack <T>
{
T[] m_Items;
public void Push(T item)
{...}
public T Pop()
{...}
}
什么是泛型?
• 通用数据结构类采用泛型实现
public public class Stack<T>
{
readonly int m_Size;
int m_StackPointer = 0;
T[] m_Items;
public Stack():this(100)
{}
public Stack(int size)
{
m_Size = size;
m_Items = new T[m_Size];
}
public void Push(T item)
{
if(m_StackPointer >= m_Size)
throw new StackOverflowException();
m_Items[m_StackPointer] = item;
m_StackPointer++;
}
public T Pop()
{
m_StackPointer--;
if(m_StackPointer >= 0)
{
return m_Items[m_StackPointer];
}
else
{
m_StackPointer = 0;
throw new InvalidOperationException(
"Cannot pop an empty stack");
}
}
}
编程模型的优点在于,内部算法和数据操作保持不变,
而实际数据类型可以在使用时指定。

泛型是如何实现的?
• 在.NET 2.0 中,泛型在IL(中间语言)和
CLR 本身中具有本机支持
• 编译泛型类时,就像编译其他类一样,泛
型仅保留一个占位符
• 而用特定类型实例化泛型代码,编译时会
将泛型替换为实例化的特定类型
泛型的好处
• 一次性地开发、测试和部署代码,通过任何类型
(包括将来的类型)来重用它
• 编译器支持和类型安全
• 不会强行对值类型进行装箱和取消装箱,或者对
引用类型进行向下强制类型转换,所以性能得到
显著提高
– 值类型,性能通常会提高200%
– 引用类型,在访问该类型时,可以预期性能最多提高
100%(当然,整个应用程序的性能可能会提高,也可
能不会提高)
应用泛型
• 因为IL 和CLR 为泛型提供本机支持,所以
大多数符合CLR 的语言都可以利用一般类

Dim stack As Stack(Of Integer)
stack = new Stack(Of Integer)
stack.Push(3)
Dim number As Integer
number = stack.Pop()
在结构中使用泛型
• 轻量级的结构中使用泛型
public struct Point<T>
{
public T X;
public T Y;
}
Point<int> point;
point.X = 1;
point.Y = 2;
Point<double> point;
point.X = 1.2;
point.Y = 3.4;
Default关键字
public T Pop()
{
m_StackPointer--;
if(m_StackPointer >= 0)
{
return m_Items[m_StackPointer];
}
else
{
m_StackPointer = 0;
throw new InvalidOperationException(
"Cannot pop an empty stack");
}
}
• 假设您不希望在堆栈为空时引发异常,而是希望返回堆栈中存储的类
型的默认值
– 值类型返回0(整型、枚举和结构)
– 引用类型返回null
• 如果是基于object,则可以简单的返回null
public T Pop()
{
m_StackPointer--;
if(m_StackPointer >= 0)
{
return m_Items[m_StackPointer];
}
else
{
m_StackPointer = 0;
return default(T);
}
}
多个泛型
• 单个类型可以定义多个泛型
class Node<K,T>
{
public K Key;
public T Item;
public Node<K,T> NextNode;
public Node()
{
Key = default(K);
Item = defualt(T);
NextNode = null;
}
public Node(K key,T item,Node<K,T> nextNode)
{
Key = key;
Item = item;
NextNode = nextNode;
}
}
public class LinkedList<K,T>
{
Node<K,T> m_Head;
public LinkedList()
{
m_Head = new Node<K,T>();
}
public void AddHead(K key,T item)
{
Node<K,T> newNode = new Node<K,T>
(key,item,m_Head.NextNode);
m_Head.NextNode = newNode;
}
}
泛型别名
using List = LinkedList<int,string>;
class ListClient
{
static void Main(string[] args)
{
List list = new List();
list.AddHead(123,"AAA");
}
}
• 在文件头部使用using为特定类型取别名
• 别名作用范围是整个文件
泛型约束-派生约束
• 在C#2.0中,所有的派生约束必须放在类的实际派生列表之后,如:
• 通常,只须在需要的级别定义约束。比如:在Node节点定义
IComparable<K>约束是没有意义的。如果一定要在Node上定义
IComparable<K>约束,则LinkedList上也要定义此约束
public class LinkedList<K,T> : IEnumerable<T> where K : IComparable<K>
{...}
class Node<K,T> where K : IComparable<K>
{...}
public class LinkedList<KeyType,DataType>
where KeyType : IComparable<KeyType>
{
Node<KeyType,DataType> m_Head;
}
泛型约束-派生约束
• 一个泛型参数上约束多个接口(彼此用逗号分隔)
public class LinkedList<K,T> where K : IComparable<K>,IConvertible
{...}
• 在一个约束中最多只能使用一个基类,这是因为C# 不支持实现的多重
继承
• 约束的基类不能是密封类或静态类,并且由编译器实施这一限制
• 不能将System.Delegate 或System.Array 约束为基类
• 可以同时约束一个基类以及一个或多个接口,但是该基类必须首先出
现在派生约束列表中
public class LinkedList<K,T> where K : MyBaseClass, IComparable<K>
{...}
泛型约束-派生约束
• C# 允许您将另一个一般类型参数指定为约束
• 定义自己的基类或接口进行泛型约束
public class MyClass<T,U> where T : U
{...}
public interface IMyInterface
{...}
public class MyClass<T> where T : IMyInterface
{...}
MyClass<IMyInterface> obj = new MyClass<IMyInterface>();
public class MyOtherClass
{...}
public class MyClass<T> where T : MyOtherClass
{...}
MyClass<MyOtherClass> obj = new MyClass<MyOtherClass>();
泛型约束-派生约束
• 自定义的接口或基类必须与泛型参数具有一致的可见性
public class MyBaseClass
{}
internal class MySubClass<T> where T : MyBaseClass
{}
internal class MyBaseClass
{}
public class MySubClass<T> where T : MyBaseClass
{}
正确的可见性
颠倒的可见性
泛型约束-构造函数约束
• 假设您要在一般类的内部实例化一个新的一般对象。问题在于,C#
编译器不知道客户端将使用的类型实参是否具有匹配的构造函数,因
而它将拒绝编译实例化行。
• 为了解决该问题,C# 允许约束一般类型参数,以使其必须支持公共
默认构造函数。这是使用new() 约束完成的。
class Node<K,T> where T : new()
{
public K Key;
public T Item;
public Node<K,T> NextNode;
public Node()
{
Key = default(K);
Item = new T();
NextNode = null;
}
}
泛型约束-构造函数约束
• 可以将构造函数约束与派生约束组合起来,前提
是构造函数约束出现在约束列表中的最后
public class LinkedList<K,T> where K : IComparable<K>,new()
{...}
泛型约束-引用/值类型约束
• 可以使用struct 约束将一般类型参数约束为值类型(例
如,int、bool 和enum),或任何自定义结构
public class MyClass<T> where T : struct
{...}
• 同样,可以使用class 约束将一般类型参数约束为引用类
型(类)
public class MyClass<T> where T : class
{...}
泛型约束-引用/值类型约束
• 不能将引用/值类型约束与基类约束一起使
用,因为基类约束涉及到类
• 不能使用结构和默认构造函数约束,因为
默认构造函数约束也涉及到类
• 虽然您可以使用类和默认构造函数约束,
但这样做没有任何价值
• 可以将引用/值类型约束与接口约束组合起
来,前提是引用/值类型约束出现在约束列
表的开头


//Questions? Comments? go to 
//http://www.idesign.net

using System;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;

namespace VS2005Demo1
{
    
public class Perf : Form
    
{
        
delegate void TestMethod();

        Button m_ValueTypesTest;
        Button m_ReferenceTypesTest;
        TextBox m_TextResultBox;
        Label m_TestResultLabel;
        TrackBar m_IterationBar;
        Label m_DurationLabel;
        
const long COUNT = 100000;

        
public Perf()
        
{
            InitializeComponent();
        }

        
Windows Form Designer generated code
        
long GetTestTime(TestMethod testMethod)
        
{
            Stopwatch stopper 
= new Stopwatch();

            stopper.Start();
            testMethod();
            stopper.Stop();

            
return stopper.ElapsedMilliseconds;
        }

        
void OnValueTest(object sender, EventArgs e)
        
{
            
float boxedTime = GetTestTime(TestValueBoxed);
            
float genericTime = GetTestTime(TestValueGeneric);

            
float perf = 100 * 1 / ((genericTime / boxedTime));

            m_TextResultBox.Text 
= Math.Round(perf).ToString() + "%";
        }

        
void OnReferenceTest(object sender, EventArgs e)
        
{
            
float boxedTime = GetTestTime(TestReferenceBoxed);
            
float genericTime = GetTestTime(TestReferenceGeneric);

            
float perf = 100 * 1 / ((genericTime / boxedTime));

            m_TextResultBox.Text 
= Math.Round(perf).ToString() + "%";
        }

        
void TestValueBoxed()
        
{
            Stack stack 
= new Stack();

            stack.Push(
1);
            stack.Push(
2);
            stack.Push(
3);
            stack.Push(
4);

            
long temp = 0;
            
long iteration = COUNT * m_IterationBar.Value;
            
for (long i = 0; i < iteration; i++)
            
{
                stack.Push(i);
                temp 
= (long)stack.Pop();
            }

        }


        
void TestValueGeneric()
        
{
            Stack
<long> stack = new Stack<long>();

            stack.Push(
1);
            stack.Push(
2);
            stack.Push(
3);
            stack.Push(
4);

            
long temp = 0;
            DateTime start 
= DateTime.Now;

            
long iteration = COUNT * m_IterationBar.Value;

            
for (long i = 0; i < iteration; i++)
            
{
                stack.Push(i);
                temp 
= stack.Pop();
            }

        }

        
void TestReferenceBoxed()
        
{
            Stack stack 
= new Stack();

            stack.Push(
"1");
            stack.Push(
"2");
            stack.Push(
"3");
            stack.Push(
"4");

            
string temp = String.Empty;

            
long iteration = COUNT * m_IterationBar.Value;

            
for (long i = 0; i < iteration; i++)
            
{
                stack.Push(
"AAAAA");
                temp 
= (string)stack.Pop();
            }

        }

        
void TestReferenceGeneric()
        
{
            Stack
<string> stack = new Stack<string>();

            stack.Push(
"1");
            stack.Push(
"2");
            stack.Push(
"3");
            stack.Push(
"4");

            
string temp = String.Empty;

            
long iteration = COUNT * m_IterationBar.Value;

            
for (long i = 0; i < iteration; i++)
            
{
                stack.Push(
"AAAAA");
                temp 
= stack.Pop();
            }

        }

    }

}

posted on 2006-07-05 17:39  公木子  阅读(414)  评论(0)    收藏  举报