2013-10-22
展开全部
在给你比较好懂的:
通过泛型可以定义类型安全类,而不会损害类型安全、性能或工作效率。您只须一次性地将服务器实现为一般服务器,同时可以用任何类型来声明和使用它。为此,需要使用 < 和 > 括号,以便将一般类型参数括起来。例如,可以按如下方式定义和使用一般堆栈:
public class Stack{ T[] m_Items; public void Push(T item) {...} public T Pop() {...}}Stack stack = new Stack();stack.Push(1);stack.Push(2);int number = stack.Pop();
代码块 2 显示一般堆栈的完整实现。将代码块 1 与代码块 2 进行比较,您会看到,好像 代码块 1 中每个使用 Object 的地方在代码块 2 中都被替换成了 T,除了使用一般类型参数 T 定义 Stack 以外:
public class Stack{...}
在使用一般堆栈时,必须通知编译器使用哪个类型来代替一般类型参数 T(无论是在声明变量时,还是在实例化变量时):
Stack stack = new Stack();
编译器和运行库负责完成其余工作。所有接受或返回 T 的方法(或属性)都将改为使用指定的类型(在上述示例中为整型)。
代码块 2. 一般堆栈
public class Stack{ 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"); } }}
注 T 是一般类型参数(或类型参数),而一般类型为 Stack。Stack 中的 int 为类型实参。
该编程模型的优点在于,内部算法和数据操作保持不变,而实际数据类型可以基于客户端使用服务器代码的方式进行更改。
泛型实现
表面上,C# 泛型的语法看起来与 C++ 模板类似,但是编译器实现和支持它们的方式存在重要差异。正如您将在后文中看到的那样,这对于泛型的使用方式具有重大意义。
注 在本文中,当提到 C++ 时,指的是传统 C++,而不是带有托管扩展的 Microsoft C++。
与 C++ 模板相比,C# 泛型可以提供增强的安全性,但是在功能方面也受到某种程度的限制。
在一些 C++ 编译器中,在您通过特定类型使用模板类之前,编译器甚至不会编译模板代码。当您确实指定了类型时,编译器会以内联方式插入代码,并且将每个出现一般类型参数的地方替换为指定的类型。此外,每当您使用特定类型时,编译器都会插入特定于该类型的代码,而不管您是否已经在应用程序中的其他某个位置为模板类指定了该类型。C++ 链接器负责解决该问题,并且并不总是有效。这可能会导致代码膨胀,从而增加加载时间和内存足迹。
在 .NET 2.0 中,泛型在 IL(中间语言)和 CLR 本身中具有本机支持。在编译一般 C# 服务器端代码时,编译器会将其编译为 IL,就像其他任何类型一样。但是,IL 只包含实际特定类型的参数或占位符。此外,一般服务器的元数据包含一般信息。
客户端编译器使用该一般元数据来支持类型安全。当客户端提供特定类型而不是一般类型参数时,客户端的编译器将用指定的类型实参来替换服务器元数据中的一般类型参数。这会向客户端的编译器提供类型特定的服务器定义,就好像从未涉及到泛型一样。这样,客户端编译器就可以确保方法参数的正确性,实施类型安全检查,甚至执行类型特定的 IntelliSense。
有趣的问题是,.NET 如何将服务器的一般 IL 编译为机器码。原来,所产生的实际机器码取决于指定的类型是值类型还是引用类型。如果客户端指定值类型,则 JIT 编译器将 IL 中的一般类型参数替换为特定的值类型,并且将其编译为本机代码。但是,JIT 编译器会跟踪它已经生成的类型特定的服务器代码。如果请求 JIT 编译器用它已经编译为机器码的值类型编译一般服务器,则它只是返回对该服务器代码的引用。因为 JIT 编译器在以后的所有场合中都将使用相同的值类型特定的服务器代码,所以不存在代码膨胀问题。
如果客户端指定引用类型,则 JIT 编译器将服务器 IL 中的一般参数替换为 Object,并将其编译为本机代码。在以后的任何针对引用类型而不是一般类型参数的请求中,都将使用该代码。请注意,采用这种方式,JIT 编译器只会重新使用实际代码。实例仍然按照它们离开托管堆的大小分配空间,并且没有强制类型转换。
泛型的好处
.NET 中的泛型使您可以重用代码以及在实现它时付出的努力。类型和内部数据可以在不导致代码膨胀的情况下更改,而不管您使用的是值类型还是引用类型。您可以一次性地开发、测试和部署代码,通过任何类型(包括将来的类型)来重用它,并且全部具有编译器支持和类型安全。因为一般代码不会强行对值类型进行装箱和取消装箱,或者对引用类型进行向下强制类型转换,所以性能得到显著提高。对于值类型,性能通常会提高 200%;对于引用类型,在访问该类型时,可以预期性能最多提高 100%(当然,整个应用程序的性能可能会提高,也可能不会提高)。本文随附的源代码包含一个微型基准应用程序,它在紧密循环中执行堆栈。该应用程序使您可以在基于 Object 的堆栈和一般堆栈上试验值类型和引用类型,以及更改循环迭代的次数以查看泛型对性能产生的影响。
通过泛型可以定义类型安全类,而不会损害类型安全、性能或工作效率。您只须一次性地将服务器实现为一般服务器,同时可以用任何类型来声明和使用它。为此,需要使用 < 和 > 括号,以便将一般类型参数括起来。例如,可以按如下方式定义和使用一般堆栈:
public class Stack{ T[] m_Items; public void Push(T item) {...} public T Pop() {...}}Stack stack = new Stack();stack.Push(1);stack.Push(2);int number = stack.Pop();
代码块 2 显示一般堆栈的完整实现。将代码块 1 与代码块 2 进行比较,您会看到,好像 代码块 1 中每个使用 Object 的地方在代码块 2 中都被替换成了 T,除了使用一般类型参数 T 定义 Stack 以外:
public class Stack{...}
在使用一般堆栈时,必须通知编译器使用哪个类型来代替一般类型参数 T(无论是在声明变量时,还是在实例化变量时):
Stack stack = new Stack();
编译器和运行库负责完成其余工作。所有接受或返回 T 的方法(或属性)都将改为使用指定的类型(在上述示例中为整型)。
代码块 2. 一般堆栈
public class Stack{ 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"); } }}
注 T 是一般类型参数(或类型参数),而一般类型为 Stack。Stack 中的 int 为类型实参。
该编程模型的优点在于,内部算法和数据操作保持不变,而实际数据类型可以基于客户端使用服务器代码的方式进行更改。
泛型实现
表面上,C# 泛型的语法看起来与 C++ 模板类似,但是编译器实现和支持它们的方式存在重要差异。正如您将在后文中看到的那样,这对于泛型的使用方式具有重大意义。
注 在本文中,当提到 C++ 时,指的是传统 C++,而不是带有托管扩展的 Microsoft C++。
与 C++ 模板相比,C# 泛型可以提供增强的安全性,但是在功能方面也受到某种程度的限制。
在一些 C++ 编译器中,在您通过特定类型使用模板类之前,编译器甚至不会编译模板代码。当您确实指定了类型时,编译器会以内联方式插入代码,并且将每个出现一般类型参数的地方替换为指定的类型。此外,每当您使用特定类型时,编译器都会插入特定于该类型的代码,而不管您是否已经在应用程序中的其他某个位置为模板类指定了该类型。C++ 链接器负责解决该问题,并且并不总是有效。这可能会导致代码膨胀,从而增加加载时间和内存足迹。
在 .NET 2.0 中,泛型在 IL(中间语言)和 CLR 本身中具有本机支持。在编译一般 C# 服务器端代码时,编译器会将其编译为 IL,就像其他任何类型一样。但是,IL 只包含实际特定类型的参数或占位符。此外,一般服务器的元数据包含一般信息。
客户端编译器使用该一般元数据来支持类型安全。当客户端提供特定类型而不是一般类型参数时,客户端的编译器将用指定的类型实参来替换服务器元数据中的一般类型参数。这会向客户端的编译器提供类型特定的服务器定义,就好像从未涉及到泛型一样。这样,客户端编译器就可以确保方法参数的正确性,实施类型安全检查,甚至执行类型特定的 IntelliSense。
有趣的问题是,.NET 如何将服务器的一般 IL 编译为机器码。原来,所产生的实际机器码取决于指定的类型是值类型还是引用类型。如果客户端指定值类型,则 JIT 编译器将 IL 中的一般类型参数替换为特定的值类型,并且将其编译为本机代码。但是,JIT 编译器会跟踪它已经生成的类型特定的服务器代码。如果请求 JIT 编译器用它已经编译为机器码的值类型编译一般服务器,则它只是返回对该服务器代码的引用。因为 JIT 编译器在以后的所有场合中都将使用相同的值类型特定的服务器代码,所以不存在代码膨胀问题。
如果客户端指定引用类型,则 JIT 编译器将服务器 IL 中的一般参数替换为 Object,并将其编译为本机代码。在以后的任何针对引用类型而不是一般类型参数的请求中,都将使用该代码。请注意,采用这种方式,JIT 编译器只会重新使用实际代码。实例仍然按照它们离开托管堆的大小分配空间,并且没有强制类型转换。
泛型的好处
.NET 中的泛型使您可以重用代码以及在实现它时付出的努力。类型和内部数据可以在不导致代码膨胀的情况下更改,而不管您使用的是值类型还是引用类型。您可以一次性地开发、测试和部署代码,通过任何类型(包括将来的类型)来重用它,并且全部具有编译器支持和类型安全。因为一般代码不会强行对值类型进行装箱和取消装箱,或者对引用类型进行向下强制类型转换,所以性能得到显著提高。对于值类型,性能通常会提高 200%;对于引用类型,在访问该类型时,可以预期性能最多提高 100%(当然,整个应用程序的性能可能会提高,也可能不会提高)。本文随附的源代码包含一个微型基准应用程序,它在紧密循环中执行堆栈。该应用程序使您可以在基于 Object 的堆栈和一般堆栈上试验值类型和引用类型,以及更改循环迭代的次数以查看泛型对性能产生的影响。
2013-10-22
展开全部
2.0 版 C# 语言和公共语言运行库 (CLR) 中增加了泛型。泛型将类型参数的概念引入 .NET Framework,类型参数使得设计如下类和方法成为可能:这些类和方法将一个或多个类型的指定推迟到客户端代码声明并实例化该类或方法的时候。例如,通过使用泛型类型参数 T,您可以编写其他客户端代码能够使用的单个类,而不致引入运行时强制转换或装箱操作的成本或风险,如下所示:
C# 复制代码
// Declare the generic class.
public class GenericList<T>
{
void Add(T input) { }
}
class TestGenericList
{
private class ExampleClass { }
static void Main()
{
// Declare a list of type int.
GenericList<int> list1 = new GenericList<int>();
// Declare a list of type string.
GenericList<string> list2 = new GenericList<string>();
// Declare a list of type ExampleClass.
GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
}
}
泛型概述
使用泛型类型可以最大限度地重用代码、保护类型的安全以及提高性能。
泛型最常见的用途是创建集合类。
.NET Framework 类库在 System.Collections.Generic 命名空间中包含几个新的泛型集合类。应尽可能地使用这些类来代替普通的类,如 System.Collections 命名空间中的 ArrayList。
您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
可以对泛型类进行约束以访问特定数据类型的方法。
关于泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。
C# 复制代码
// Declare the generic class.
public class GenericList<T>
{
void Add(T input) { }
}
class TestGenericList
{
private class ExampleClass { }
static void Main()
{
// Declare a list of type int.
GenericList<int> list1 = new GenericList<int>();
// Declare a list of type string.
GenericList<string> list2 = new GenericList<string>();
// Declare a list of type ExampleClass.
GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
}
}
泛型概述
使用泛型类型可以最大限度地重用代码、保护类型的安全以及提高性能。
泛型最常见的用途是创建集合类。
.NET Framework 类库在 System.Collections.Generic 命名空间中包含几个新的泛型集合类。应尽可能地使用这些类来代替普通的类,如 System.Collections 命名空间中的 ArrayList。
您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
可以对泛型类进行约束以访问特定数据类型的方法。
关于泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
2013-10-22
展开全部
下面的代码示例演示客户端代码如何使用泛型 GenericList<T> 类来创建整数列表。只需更改类型参数,即可方便地修改下面的代码示例,创建字符串或任何其他自定义类型的列表:
C# 复制代码
class TestGenericList
{
static void Main()
{
// int is the type argument
GenericList<int> list = new GenericList<int>();
for (int x = 0; x < 10; x++)
{
list.AddHead(x);
}
foreach (int i in list)
{
System.Console.Write(i + " ");
}
System.Console.WriteLine("\nDone");
}
}
C# 复制代码
class TestGenericList
{
static void Main()
{
// int is the type argument
GenericList<int> list = new GenericList<int>();
for (int x = 0; x < 10; x++)
{
list.AddHead(x);
}
foreach (int i in list)
{
System.Console.Write(i + " ");
}
System.Console.WriteLine("\nDone");
}
}
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
2013-10-22
展开全部
泛型编程让你编写完全一般化并可重复使用的算法,其效率与针对某特定数据类型而设计的算法相同。泛型编程的代表作品STL是一种高效、泛型、可交互操作的软件组件。所谓泛型(Genericity),是指具有在多种数据类型上皆可操作的含意,与模板有些相似。STL巨大,而且可以扩充,它包含很多计算机基本算法和数据结构,而且将算法与数据结构完全分离,其中算法是泛型的,不与任何特定数据结构或对象类型系在一起。STL以迭代器(Iterators)和容器(Containers)为基础,是一种泛型算法(Generic Algorithms)库,容器的存在使这些算法有东西可以操作。STL包含各种泛型算法(algorithms)、泛型指针(iterators)、泛型容器(containers)以及函数对象(function objects)。STL并非只是一些有用组件的集合,它是描述软件组件抽象需求条件的一个正规而有条理的架构。
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询