Effective C#之Item 49:Prepare for C# 2.0

  rel="File-List" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml"> rel="themeData" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx"> rel="colorSchemeMapping" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml">

Item 49: Prepare for C# 2.0

C#2.0最好准备

C# 2.0, available in 2005, will have some major new features in the C# language. Some of today's best practices will change with the tools that will be available in the next release. Although you might not be using these features just yet, you should prepare for them.

C#2.0,在2005年会可用,它将在C#语言方面有一些主要的新特性。随着下次发布带来的工具,今天的一些最好的实践可能会改变。虽然你可能还没有使用这些特性,但是你需要做好准备。

When Visual Studio .NET 2005 is released, you will get a new, upgraded C# language. The additions to the language are sure to make you a more productive developer: You'll be able to write more reusable code and higher-level constructs in fewer lines of source. All in all, you'll get more done faster.

VS2005发布时,你将得到新的更新过的C#语言。对该语言的增强,确定会使你成为更具生产力的开发者:你将可以使用更少的源代码就能编写更可复用的代码,更高级别的结构体。总之一句话,你可以更快的完成更多。

C# 2.0 has four major new features: generics, iterators, anonymous methods, and partial types. The focus of these new features is to increase your productivity as a C# developer. This item discusses three of those features and how you should prepare for them now. Generics will have more impact on how you develop software than any of the other new features in C#. The generics feature is not specific to C#. To implement C# generics, Microsoft is extending the CLR and Microsoft Intermediate Language (MSIL) as well as the C# language. C#, Managed C++, and VB .NET will be capable of creating generics. J# will be capable of consuming them.

C#2.04个主要的特性:泛型、迭代、匿名方法和分部类型。这些新特性的关注点就是增强你(作为开发者)的生产力。本条款讨论这些特性中的三个,以及你应该如何现在就做好准备。泛型和C#的其它新特性相比,在你如何开发软件方面,有更重要的影响。泛型特征并不是C#专有的。为了实现C#泛型,微软对CLRMSIL以及C#语言进行了扩展。C#、托管C++VB.NET都会有创建泛型的能力。J#在这方面则会进行加强。

Generics provide "parametric polymorphism," which is a fancy way of saying you that create a series of similar classes from a single source. The compiler generates different versions when you provide a specific type for a generic parameter. You use generics to build algorithms that are parameterized with respect to the structures they act upon. You can find great candidates for generics in the .NET Collections namespace: HashTables, ArrayLists, Queue, and Stack all can store different object types without affecting their implementation. These collections are such good candidates that the 2.0 release of the .NET Framework will include the System.Collections.Generic namespace containing generic counterparts for all the current collection classes. C# 1.0 stores reference to the System.Object type. Although the current design is reusable for all types, it has many deficiencies and is not type-safe. Consider this code:

泛型提供了“参数多态”,这很有趣,也就是说,你可以从一个单独的源创建一系列相似的类。当你为泛型参数提供一个特定类型时,编译器会生成不同的版本。使用泛型构建这样的算法:算法运行在相关的参数化的结构上。在.NETCollections命名空间下,可以找到泛型的很好的候选者:HashTablesArrayListsQueueStack,它们都能存储不同的对象类型,而不影响实现。这些集合都是.NET2.0框架很好的候选者,在System.Collections.Generic命名空间下面将包含所有这些集合类的泛型版本。C#1.0存储System.Object的引用。虽然当前的设计对所有的类型都可用,但是它有很多缺点,并且不是类型安全的。考虑这些代码:

  1. ArrayList myIntList = new ArrayList( );
  2. myIntList.Add(32 );
  3. myIntList.Add(98.6 );
  4. myIntList.Add("Bill Wagner" );

 

This compiles just fine, but it almost certainly is not the intent. Did you really create a design that calls for a container that holds totally disparate items? Or were you working around a limitation in the language? This practice means that when you remove items from the collection, you must add extra code to determine what kind of objects were put on the list in the first place. In all cases, you need to cast items from System.Object to the particular type you placed on the list.

这会编译通过,但是几乎不能表达你的意图。对于包含了完全不同的元素的容器,你确实需要这样的设计么?或者说,你是否在和该语言的限制一起工作呢?这个练习意味着,当你从集合里面移除元素的时候,需要添加额外的代码来决定前面你将什么类型的代码放到了list里面。对于所有这些情况,需要将System.Object类型的元素转换成你放入list的特定类型。

But that's not all. Value types pay a particular penalty when they are placed in these 1.0-style collections. Anytime you put a value type in a collection, you must store it in a box. You pay again to remove the item from the box when you access an element in the collection. This penalty is small, but with large collections of thousands of items, it adds up quickly. Generics remove this penalty by generating specific object code for each value type.

但是还不止这些。当值类型被放在1.0风格的集合中时,是需要付出代价的。无论何时你将值类型放到大集合中时,都需要对其进行装箱。当你访问集合中的一个元素时,需要付出拆箱的代价。代价很小,但是对于有上千个元素的大集合来说,累加效果是很明显的。泛型则除去了为每个值类型生成特定对象的代价。

Those of you familiar with C++ templates will have no trouble working with C# generics because the syntax is very similar. The inner workings for generics, however, are quite different. Let's look at one simple example to see how generics work and how they are implemented. Consider this portion of a list class:

熟悉C++模板的人,在C#泛型方面不会有什么麻烦,因为语法很相似。但是,泛型的内部工作机制是很不同的让我们看一个简单的例子,看看泛型是如何工作和实现的。考虑一个list类的一部份:

  1. public class List
  2. {
  3.   internal class Node
  4.   {
  5.     internal object val;
  6.     internal Node next;
  7.   }
  8.   private Node first;
  9.  
  10.  public void AddHead( object t )
  11.   {
  12.     // ...
  13.   }
  14.  
  15.   public object Head()
  16.   {
  17.     return first.val;
  18.   }
  19.  
  20. }
  21.  

This code stores System.Object references in its collection. Anytime you use it, you must add casts on the objects accessed from the collection. But using C# generics, you define the same class like this:

上面代码在集合里面存储了System.Object引用。任何时候,你使用它时,应该对集合里面被访问的对象进行强制转换。但是使用C#泛型,就可以像这样定义类:

  1. public class List < ItemType >
  2. {
  3.   private class Node < ItemType >
  4.   {
  5.     internal ItemType val;
  6.     internal Node < ItemType > next;
  7.   }
  8.   private Node < ItemType > first;
  9.  
  10.   public void AddHead( ItemType t )
  11.   {
  12.     // ...
  13.   }
  14.  
  15.   public ItemType Head( )
  16.   {
  17.     return first.val;
  18.   }
  19. }

 

You replace object with ItemType, the parameter type in the class definition. The C# compiler replaces ItemType with the proper type when you instantiate the list. For example, take a look at this code:

在类的定义中,对于参数类型,使用ItemType替换了object。当你对list进行初始化的时候,C#编译器将使用合适的类型对ItemType进行替换。例如,看这个代码:

  1. List < int > intList = new List < int >();

 

The MSIL generated specifies that intList stores integer sand only integers. Generics have several advantages over the implementations you can create today. For starters, the C# compiler reports compile-time errors if you attempt anything but an integer in the collection; today, you need to catch those errors by testing the code at runtime.

生成的MSIL指定intList存储且只能存储整型。对于你今天创建的实现,泛型有一些优势。对于新手,如果你尝试向集合里存储不是整型的其它任何东西,C#编译器都会报编译时错误;而在现在,你需要在运行时通过检测代码来捕捉这些错误。

In C# 1.0, you pay the boxing and unboxing penalty whenever you move a value type into or out of a collection that stores System.Object references. Using generics, the JIT compiler creates a specific instance of the collection that stores a particular value type; you don't need to box or unbox the items. But there's more to it. The C# designers want to avoid the code bloat often associated with C++ templates. To save space, the JIT compiler generates only one version of the type for all reference types. This provides a size/speed trade-off whereby value types get a specific version of each type (avoiding boxing), and reference types share a single runtime version storing System.Object (avoiding code bloat). The compiler still reports errors when the wrong reference type is used with these collections.

C#1.0里面,当你向存储System.Object引用的集合中添加或者移除一个值类型时,都需要付出装箱或者拆箱的代价。使用泛型,JTI编译器就创建了存储特定值类型的集合的特定实例;你不需要对元素进行装箱和拆箱。但是不止这些优点。C#设计者想要避免C++模板里面常常会出现的代码肿胀。为了节省空间,JIT编译器为所有的引用类型只生成该类型的一个版本。这是一个空间和速度的折衷:值类型得到了该类型的特定版本,避免了装箱;引用类型共享一个存储System.Object的单独的运行时版本,避免了代码肿胀。当这些集合里面使用了错误的引用类型时,编译器会报错。

To implement generics, the CLR and the MSIL language undergo some changes. When you compile a generic class, MSIL contains placeholders for each parameterized type. Consider these two method declarations in MSIL:

为了实现泛型,CLRMSIL语言经历了一些修改。当你编译泛型类时,MSIL为每个参数化的类型包含了占位符。考虑在MSIL里面声明的这两个方法:

  1. .method public AddHead (!0 t) {
  2.  }
  3.  
  4. .method public !0 Head () {
  5. }
  6.  

!0 is a placeholder for a type to be created when a particular instantiation is declared and created. Here's one possible replacement:

!0就是一个类型的占位符,当一个特定的初始化被声明和创建时,该类型就被创建。这也是一个可能的替换:

  1. .method public AddHead (System.Int32 t) {
  2.  }
  3.  
  4. .method public System.Int32 Head () {
  5. }
  6.  

Similarly, variable instantiations contain the specific type. The previous declaration for a list of integers becomes this:

简单来讲,可变化的初始化,包含了指定的类型。前面对于整型list的声明就变成了这样:

  1. .locals (class List<int>)
  2. newobj void List<int>::.ctor ()
  3.  

This illustrates the way the C# compiler and the JIT compiler work together for generics. The C# compiler generates MSIL that contains placeholders for each type parameter. The JIT compiler turns these placeholders into specific types either System.Object for all reference types, or specific value types for each value type. Each variable instantiation of a generic type includes type information so the C# compiler can enforce type safety.

上面展示了C#编译器和JIT编译器共同为泛型工作的方式。C#编译器生成的MSIL为每个类型参数包含了占位符。JIT编译器将这些占位符转换成特定的类型:对于所有的引用类型都是System.Object;对于值类型就是特定的值类型。泛型类型的每个可变化的初始化,包含了类型信息,因此C#编译器保证了类型安全:

Constraint definitions for generics will have a large impact on how you prepare for generics. Remember that a specific instantiation of a generic runtime class does not get created until the CLR loads and creates that instantiation at runtime. To generate MSIL for all possible instantiations of a generic class, the compiler needs to know the capabilities of the parameterized type in the generic classes you create. The C# solution for this problem is constraints. Constraints declare expected capabilities on the parameterized type. Consider a generic implementation of a binary tree. Binary trees store objects in sorted order; therefore, a binary tree can store only types that implement IComparable. You can specify this requirement using constraints:

泛型的约束定义对你如何为泛型做准备有很大的影响。记住,泛型运行时类的特定实例,只有CLR在运行时加载并创建该实例时,才被创建。为了生成泛型类所有实例的MSIL,编译器需要知道,在你创建的泛型类中,参数化类型的能力。对于该问题的C#解决方案就是约束。约束宣布了参数化类型的能力。考虑二叉树的泛型实现。二叉树存储排好序的对象;因此,二叉树仅能存储实现了IComparable的类型。你可以通过使用约束来指定该要求:

  1. public class BinaryTree < ValType > where ValType : IComparable < ValType >
  2. {
  3. }

Using this definition, any instantiation of BinaryTree using a class that does not support the IComparable interface won't compile. You can specify multiple constraints. Suppose that you want to limit your BinaryTree to objects that support ISerializable. You simply add more constraints. Notice that interfaces and constraints can be generic types as well:

使用该定义,任何使用了不支持IComparable接口的类来实例化的BinaryTree,将不能通过编译。你可以指定多重约束。假设,你希望限制BinaryTree来支持ISerializable,那么就可以添加更多的约束。注意,接口和限制都可以是泛型类。

  1. public class BinaryTree < ValType > where ValType : IComparable < ValType > , ValType : ISerializable
  2. {
  3. }

 

You can specify one base class and any number of interfaces as a set of constraints for each parameterized type. In addition, you can specify that a type must have a parameterless constructor.

对于每个参数化的类型,可以指定一个基类和任意数量的接口,作为约束集合。另外,可以指定类型必须有无参数的构造函数。

Constraints also provide one more advantage: The compiler assumes that the objects in your generic class support any interfaces (or base class methods) specified in the constraint list. In the absence of any constraints, the compiler assumes only the methods defined in System.Object. You would need to add casts to use any other method. Whenever you use a method that is not defined in System.Object, you should document those requirements in a set of constraints.

约束也提供了另外的优势:编译器会假设泛型类里面的对象支持任何约束列表中的接口(或基类方法)。没有任何限制的话,编译器仅假设你支持System.Object里面定义的方法。可能需要添加任何强制转化来使用任何其它方法。当你使用在System.Object里面没有定义的方法时,应该在约束里面对这些要求进行表述。

Constraints point out yet another reason to use interfaces liberally (see Item 19): It will be relatively easy to define constraints if you have defined your functionality using interfaces.

约束指出了使用合法接口的另一个原因(Item 19):如果你使用接口已经定义了你的功能,那么定义约束是相当简单的。

Iterators are a new syntax to create a common idiom using much less code. Imagine that you create some specialized new container class. To support your users, you need to create methods that support traversing this collection and returning the objects in the collection.

迭代是使用更少代码的创建通用习惯的新语法。假设你创建一些特性的新的容器类。为了给用户提供支持,你需要创建一些方法,支持遍历集合以及返回集合里面的对象。

Today, you would do this by creating a class that implements IEnumerator. IEnumerator contains two methods Reset and MoveNextand one property: Current. In addition, you would add IEnumerable to the list of implemented interfaces on your collection, and its GetEnumerator method would return an IEnumerator for your collection. By the time you're done, you have written an extra class with at least three functions, as well as some state management and another method in your main class. To illustrate this, you must write this page of code today to handle list enumeration:

今天,你可以通过创建实现了IEnumerator的类来做到这些。IEnumerator包含两个方法Reset MoveNextand,一个属性:Current。另外,你可以在你的集合里面向实现的接口添加IEnumerable,它的GetEnumerator方法将为你的集合返回一个IEnumerator。到现在为止,和一些状态管理及你的主类里面的另一个方法一起,你至少使用了3种方法来编写了一个额外的类。为了表述这些,你应该编写这样的代码来处理枚举列表:

  1. public class List : IEnumerable
  2. {
  3.   internal class ListEnumerator : IEnumerator
  4.   {
  5.     List theList;
  6.     int pos = -1;
  7.  
  8.     internal ListEnumerator( List l )
  9.     {
  10.       theList = l;
  11.     }
  12.  
  13.     public object Current
  14.     {
  15.       get
  16.       {
  17.         return theList [ pos ];
  18.       }
  19.     }
  20.  
  21.     public bool MoveNext( )
  22.     {
  23.       pos++;
  24.       return pos < theList.Length;
  25.     }
  26.  
  27.     public void Reset( )
  28.     {
  29.       pos = -1;
  30.     }
  31.   }
  32.   public IEnumerator GetEnumerator()
  33.   {
  34.     return new ListEnumerator( this );
  35.   }
  36.  
  37.   // Other methods removed.
  38. }
  39.  

C# 2.0 adds new syntax in the yield keyword that lets you write these iterators more concisely. Here is the C# 2.0 version of the previous code:

C#2.0使用yield关键字添加了新的语法,让你更明晰的编写这些迭代器。这是前面代码的C#2.0版本:

  1. public class List
  2. {
  3.   public object iterate()
  4.   {
  5.     int i=0;
  6.     while ( i < theList.Length ( ) )
  7.       yield theList [ i++ ];
  8.   }
  9.  
  10.   // Other methods removed.
  11. }

 

The yield statement lets you replace roughly 30 lines of code with only 6. This means fewer bugs, less development time, and less source code to maintain all good things.

yield表达式让你使用6行代码替换了大概30行代码。这意味着更少的bug,更少的开发时间,更少的维护代码,都是好事情。

Internally, the compiler generates the MSIL that corresponds to those 30 lines of code in today's version. The compiler does it so you don't have to. The compiler generates a class that implements the IEnumerator interface and adds it to your list of supported interfaces.

在内部,编译器生成的MSIL代码与当前30行版本的代码是一致的。编译器会这样做,因此你不再需要那么做了。编译器生成的类,实现了IEnumerator接口,把它加到你的列表内部来支持接口。

The last major new feature is partial types. Partial types let you split a C# class implementation into more than one source file. You rarely, if ever, will use this feature yourself to create multiple source files in your daily development. Microsoft proposed this change to C# to support IDEs and code generators. Today, you get a region in your form classes that contains all the code created by the VS .NET designer. In the future, these tools should create partial classes and place the code in a separate file.

最后一个主要的新特征是分部类型。分部类型让你将一个C#类的实现拆分到多于一个的源文件里面。你可能很少这样做,如果有,你可以自己使用该特性在你的日常开发中,来创建多个源文件。微软提议修改C#来支持IDE和代码生成器。今天,你在form类里面获得了一个部分,包含了由VS.NET设计器生成的代码。以后,这些工具应该创建分部类,在单独的文件里面放置这些代码:

To use this feature, you add the partial keyword to your class declaration:

为了利用该特性,需要在你的类声明里面添加partial关键字:

  1. public partial class Form1
  2. {
  3.   // Wizard Code:
  4.   private void InitializeComponent()
  5.   {
  6.     // wizard code...
  7.   }
  8. }
  9.  
  10. // In another file:
  11. public partial class Form1
  12. {
  13.   public void Method ()
  14.   {
  15.     // etc...
  16.   }
  17. }

 

Partial types do have some limitations. They are a source-only feature there is no difference in the MSIL generated from a single source file and one generated from multiple files. You also need to compile all files that form a complete type into the same assembly, and there is no automated way to ensure that you have added all the source files that form a complete class definition to your builds. You can introduce any number of problems when you split your class definition into multiple files, so I recommend that you use this feature only when IDEs generate the source using partial types. This includes forms, as I've shown earlier. VS .NET also generates partial types for typed DataSets (see Item 41) and web service proxies, so you can add your own members to those classes.

分部类型没有任何限制。这仅仅是源代码上的特性,由多个文件生成的MSIL和由一个单独源文件生成的MSIL没有任何不同。你也需要将所有组成一个完整类型的类编译成同样的程序集中。没有自动的方式可以保证:将所有组成一个完整类型的文件都添加到编译当中。当你将自己的类分割成多个文件时,你可能会引入任何数量的问题,因此我建议:只有当IDE生成使用分部类型的源代码时,你才使用该特性。这包括了我前面已经展示过的窗体。VS.NET也为类型化的DataSet以及web服务代理生成分部类型(Item41),因此你可以向这些类添加自己的成员。

I did not cover a number of C# 2.0 features because their addition will have less of an impact on how you write code today. You can make it easier to use your types with generics by defining interfaces to describe behavior: Those interfaces can be used as constraints. The new iterator syntax will provide a more efficient way to implement enumerations. You can easily replace nested enumerators with this new syntax quickly. However, custom external classes will not be so easy to replace. Develop your code now with an eye toward where you will leverage these features, and it will be easier to upgrade your existing code with new C# 2.0 features with minimal work.

我没有涵盖很多C#2.0的特性,因为将它们添加进来,对你现在如何编写代码只会有很少的影响。对于使用泛型的类,虽然这会简单点,但是你可以通过定义接口来描述其行为。那些接口可以作为约束来使用。新的迭代器语法将为实现迭代提供更高效的方法。你可以使用新语法快速的替换内嵌的枚举器。然而,用户的内部类将不会那么容易的被替换。现在开发代码的时候,就要对以后怎么利用这些特性睁一只眼,在以后,只需要很少的工作,你就能使用新的C#2.0特性来更新现存的代码。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值