ebook Addison -Essential CSharp 4.0_5 pot

98 498 0
ebook Addison -Essential CSharp 4.0_5 pot

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

ptg Constraints 443 implement the IComparable<T> interface. The syntax for this appears in Listing 11.22. Listing 11.22: Declaring an Interface Constraint public class BinaryTree<T> { public Pair<BinaryTree<T>> SubItems { get{ return _SubItems; } set { IComparable<T> first; { // first is less than second } else { // second is less than or equal to first. } _SubItems = value; } } private Pair<BinaryTree<T>> _SubItems; } Given the interface constraint addition in Listing 11.22, the compiler ensures that each time you use the BinaryTree class you specify a type parameter that implements the IComparable<T> interface. Furthermore, you no longer need to explicitly cast the variable to an IComparable<T> interface before calling the CompareTo() method. Casting is not even required to access members that use explicit interface implementation, which in other contexts would hide the member without a cast. To resolve what member to call, the compiler first checks class members directly, and then looks at the explicit interface members. If no constraint resolves the argument, only members of object are allowable. where T: System.IComparable<T> // Notice that the cast can now be eliminated. first = value.First.Item; if (first.CompareTo(value.Second.Item) < 0) From the Library of Wow! eBook ptg Chapter 11: Generics444 If you tried to create a BinaryTree<T> variable using System. Text.StringBuilder as the type parameter, you would receive a compiler error because StringBuilder does not implement IComparable<T>. The error is similar to the one shown in Output 11.3. To specify an interface for the constraint you declare an interface con- straint. This constraint even circumvents the need to cast in order to call an explicit interface member implementation. Base Class Constraints Sometimes you might want to limit the constructed type to a particular class derivation. You do this using a base class constraint, as shown in Listing 11.23. Listing 11.23: Declaring a Base Class Constraint public class EntityDictionary<TKey, TValue> : System.Collections.Generic.Dictionary<TKey, TValue> where TValue : EntityBase { } In contrast to System.Collections.Generic.Dictionary<TKey, TValue> on its own, EntityDictionary<TKey, TValue> requires that all TValue types derive from the EntityBase class. By requiring the derivation, it is possible to always perform a cast operation within the generic implemen- tation, because the constraint will ensure that all type parameters derive from the base and, therefore, that all TValue type parameters used with EntityDictionary can be implicitly converted to the base. The syntax for the base class constraint is the same as that for the inter- face constraint, except that base class constraints must appear first when multiple constraints are specified. However, unlike interface constraints, OUTPUT 11.3: error CS0309: The type ’System.Text.StringBuilder’ must be convertible to ’System.IComparable<T>’ in order to use it as parameter ’T’ in the generic type or method ’BinaryTree<T>’ From the Library of Wow! eBook ptg Constraints 445 multiple base class constraints are not allowed since it is not possible to derive from multiple classes. Similarly, base class constraints cannot be specified for sealed classes or specific structs. For example, C# does not allow a constraint for a type parameter to be derived from string or Sys- tem.Nullable<T>. struct/class Constraints Another valuable generic constraint is the ability to restrict type parame- ters to a value type or a reference type. The compiler does not allow speci- fying System.ValueType as the base class in a constraint. Instead, C# provides special syntax that works for reference types as well. Instead of specifying a class from which T must derive, you simply use the keyword struct or class, as shown in Listing 11.24. Listing 11.24: Specifying the Type Parameter As a Value Type public struct Nullable<T> : IFormattable, IComparable, IComparable<Nullable<T>>, INullable { // } Because a base class constraint requires a particular base class, using struct or class with a base class constraint would be pointless, and in fact could allow for conflicting constraints. Therefore, you cannot use struct and class constraints with a base class constraint. There is one special characteristic for the struct constraint. It limits possi- ble type parameters as being only value types while at the same time prevent- ing type parameters that are System.Nullable<T> type parameters. Why? Without this last restriction, it would be possible to define the nonsense type Nullable<Nullable<T>>, which is nonsense because Nullable<T> on its own allows a value type variable that supports nulls, so a nullable-nullable type becomes meaningless. Since the nullable operator (?) is a C# shortcut for declaring a nullable value type, the Nullable<T> restriction provided by the struct constraint also prevents code such as the following: int?? number // Equivalent to Nullable<Nullable<int> if allowed where T : struct From the Library of Wow! eBook ptg Chapter 11: Generics446 Multiple Constraints For any given type parameter, you may specify any number of interfaces as constraints, but no more than one class, just as a class may implement any number of interfaces but inherit from only one other class. Each new constraint is declared in a comma-delimited list following the generic type and a colon. If there is more than one type parameter, each must be pre- ceded by the where keyword. In Listing 11.25, the EntityDictionary class contains two type parameters: TKey and TValue. The TKey type parameter has two interface constraints, and the TValue type parameter has one base class constraint. Listing 11.25: Specifying Multiple Constraints public class EntityDictionary<TKey, TValue> : Dictionary<TKey, TValue> where TKey : IComparable<TKey>, IFormattable where TValue : EntityBase { } In this case, there are multiple constraints on TKey itself and an additional constraint on TValue. When specifying multiple constraints on one type parameter, an AND relationship is assumed. TKey must implement ICom- parable<TKey> and IFormattable, for example. Notice there is no comma between each where clause. Constructor Constraints In some cases, it is desirable to create an instance of a type parameter inside the generic class. In Listing 11.26, the New() method for the EntityDictionary<TKey, TValue> class must create an instance of the type parameter TValue. Listing 11.26: Requiring a Default Constructor Constraint public class EntityBase<TKey> { public TKey Key { get{ return _Key; } set{ _Key = value; } } From the Library of Wow! eBook ptg Constraints 447 private TKey _Key; } public class EntityDictionary<TKey, TValue> : Dictionary<TKey, TValue> where TKey: IComparable<TKey>, IFormattable { // public TValue New(TKey key) { newEntity.Key = key; Add(newEntity.Key, newEntity); return newEntity; } // } Because not all objects are guaranteed to have public default constructors, the compiler does not allow you to call the default constructor on the type parameter. To override this compiler restriction, you add the text new() after all other constraints are specified. This text is a constructor constraint, and it forces the type parameter decorated with the constructor constraint to have a default constructor. Only the default constructor constraint is available. You cannot specify a constraint for a constructor with parameters. Constraint Inheritance Constraints are inherited by a derived class, but they must be specified explicitly on the derived class. Consider Listing 11.27. Listing 11.27: Inherited Constraints Specified Explicitly class EntityBase<T> where T : IComparable<T> { // } // ERROR: // The type 'T' must be convertible to 'System.IComparable<T>' // in order to use it as parameter 'T' in the generic type or // method. where TValue : EntityBase<TKey>, new() TValue newEntity = new TValue(); From the Library of Wow! eBook ptg Chapter 11: Generics448 // class Entity<T> : EntityBase<T> // { // // } Because EntityBase requires that T implement IComparable<T>, the Entity class needs to explicitly include the same constraint. Failure to do so will result in a compile error. This increases a programmer’s awareness of the constraint in the derived class, avoiding confusion when using the derived class and discovering the constraint but not understanding where it comes from. In contrast, constraints on generic override (or explicit interface) meth- ods are inherited implicitly and may not be restated (see Listing 11.28). Listing 11.28: Inherited Constraints Specified Explicitly class EntityBase<T> where T : IComparable<T> { public virtual void Method<T>(T t) where T : IComparable<T> { // } } class Entity<T> : EntityBase<T> { public virtual void Method<T>(T t) // Error: Constraints may not be // repeated on overriding members where T : IComparable<T> { // } } In the inheritance case the type parameter on the base class can be addi- tionally constrained by adding not only the constraints on the base class (required), but also additional constraints as well. However, overriding members need to conform to the “interface” defined in the base class method. Additional constraints could break polymorphism, so they are not allowed and the type parameter constraints on the override method are implied. From the Library of Wow! eBook ptg Constraints 449 ADVANCED TOPIC Constraint Limitations Constraints are appropriately limited to avoid nonsense code. For exam- ple, you cannot combine a base class constraint with a struct or class con- straint, nor can you use Nullable<T> on struct constraint type parameters. Also, you cannot specify constraints to restrict inheritance to special types such as object, arrays, System.ValueType, System.Enum (enum), Sys- tem.Delegate, and System.MulticastDelegate. In some cases, constraint limitations are perhaps more desirable, but they still are not supported. The following subsections provide some addi- tional examples of constraints that are not allowed. Operator Constraints Are Not Allowed Another restriction on constraints is that you cannot specify a constraint that a class supports on a particular method or operator, unless that method or operator is on an interface. Because of this, the generic Add() in Listing 11.29 does not work. Listing 11.29: Constraint Expressions Cannot Require Operators public abstract class MathEx<T> { public static T Add(T first, T second) { // Error: Operator '+' cannot be applied to // operands of type 'T' and 'T'. } } In this case, the method assumes that the + operator is available on all types. However, because all types support only the methods of object (which does not include the + operator), an error occurs. Unfortunately, there is no way to specify the + operator within a constraint; therefore, creating an add method in this way is a lot more cumbersome. One rea- son for this limitation is that there is no way to constrain a type to have a static method. You cannot, for example, specify static methods on an interface. return first + second ; From the Library of Wow! eBook ptg Chapter 11: Generics450 OR Criteria Are Not Supported If you supply multiple interfaces or class constraints for a type parameter, the compiler always assumes an AND relationship between constraints. For example, where T : IComparable<T>, IFormattable requires that both IComparable<T> and IFormattable are supported. There is no way to specify an OR relationship between constraints. Hence, an equivalent of Listing 11.30 is not supported. Listing 11.30: Combining Constraints Using an OR Relationship Is Not Allowed public class BinaryTree<T> // Error: OR is not supported. where T: System.IComparable<T> || System.IFormattable { } Supporting this would prevent the compiler from resolving which method to call at compile time. Constraints of Type Delegate and Enum Are Not Valid Readers who are already familiar with C# 1.0 and are reading this chapter to learn newer features will be familiar with the concept of delegates, which are covered in Chapter 12. One additional constraint that is not allowed is the use of any delegate type as a class constraint. For example, the compiler will output an error for the class declaration in Listing 11.31. Listing 11.31: Inheritance Constraints Cannot Be of Type System.Delegate // Error: Constraint cannot be special class 'System.Delegate' public class Publisher<T> where T : System.Delegate { public event T Event; public void Publish() { if (Event != null) { Event(this, new EventArgs()); } } } All delegate types are considered special classes that cannot be specified as type parameters. Doing so would prevent compile-time validation on the From the Library of Wow! eBook ptg Constraints 451 call to Event() because the signature of the event firing is unknown with the data types System.Delegate and System.MulticastDelegate. The same restriction occurs for any enum type. Constructor Constraints Are Allowed Only for Default Constructors Listing 11.26 includes a constructor constraint that forces TValue to sup- port a default constructor. There is no constraint to force TValue to support a constructor other than the default. For example, it is not possible to make EntityBase.Key protected and only set it in a TValue constructor that takes a TKey parameter using constraints alone. Listing 11.32 demonstrates the invalid code. Listing 11.32: Constructor Constraints Can Be Specified Only for Default Constructors public TValue New(TKey key) { Add(newEntity.Key, newEntity); return newEntity; } One way to circumvent this restriction is to supply a factory interface that includes a method for instantiating the type. The factory implementing the interface takes responsibility for instantiating the entity rather than the EntityDictionary itself (see Listing 11.33). Listing 11.33: Using a Factory Interface in Place of a Constructor Constraint public class EntityBase<TKey> { public TKey Key { get { return _key; } set { _key = value; } } private TKey _key; } public class EntityDictionary<TKey, TValue, TFactory> : Dictionary<TKey, TValue> where TKey : IComparable<T>, IFormattable // Error: 'TValue': Cannot provide arguments // when creating an instance of a variable type. TValue newEntity = null; // newEntity = new TValue(key); public EntityBase(TKey key) { Key = key; } From the Library of Wow! eBook ptg Chapter 11: Generics452 { public TValue New(TKey key) { Add(newEntity.Key, newEntity); return newEntity; } } A declaration such as this allows you to pass the new key to a TValue con- structor that takes parameters rather than the default constructor. It no longer uses the constructor constraint on TValue because TFactory is responsible for instantiating the order instead of EntityDictionary< >. (One modification to the code in Listing 11.33 would be to save a copy of the factory. This would enable you to reuse the factory instead of reinstan- tiating it every time.) A declaration for a variable of type EntityDictionary<TKey, TValue, TFactory> would result in an entity declaration similar to the Order entity in Listing 11.34. Listing 11.34: Declaring an Entity to Be Used in EntityDictionary< > public class Order : EntityBase<Guid> { public Order(Guid key) : base(key) { // } } public class OrderFactory : IEntityFactory<Guid, Order> { public Order CreateNew(Guid key) { return new Order(key); } } where TValue : EntityBase<TKey> where TFactory : IEntityFactory<TKey, TValue>, new() TValue newEntity = new TFactory().CreateNew(key); public interface IEntityFactory<TKey, TValue> { TValue CreateNew(TKey key); } From the Library of Wow! eBook [...]... members) In so doing, the covariance problem just described would not occur (see Listing 11.42) Listing 11.42: Potentially Possible Covariance interface IReadOnlyPair { T First { get; } T Second { get; } } interface IPair { T First { get; set; } T Second { get; set; } } From the Library of Wow! eBook Covariance and Contravariance 459 public struct Pair : IPair, IReadOnlyPair { // } class... The output appears in Output 11.5 Listing 11.37: Inferring the Type Parameter Console.WriteLine( MathEx.Max(7, 490)); Console.WriteLine( MathEx.Min("R.O.U.S'", "Fireswamp")); From the Library of Wow! eBook Generic Methods 455 OUTPUT 11.5: 490 Fireswamp For type inferencing to be successful, the types must match the method signature However, starting with C# 3.0, the compiler added an enhancement to... ' '), tree.Item.ToString()); if (tree.SubItems.First != null) Show(tree.SubItems.First, indent+1); if (tree.SubItems.Second != null) Show(tree.SubItems.Second, indent+1); } } From the Library of Wow! eBook 456 Chapter 11: Generics Notice that the Show implementation itself does not use the IComparable interface Recall, however, that the BinaryTree class did require this (see Listing 11.39)... Deserialization.Deserialize(stream, formatter); The problem with this code is that to the user of the method, Deserialize() appears to be strongly typed However, a cast operation is still From the Library of Wow! eBook Covariance and Contravariance 457 performed implicitly rather than explicitly, as in the case of the nongeneric equivalent shown here: string greeting = (string)Deserialization.Deserialize(stream,... would allow the following (see Listing 11.41) Listing 11.41: Preventing Covariance Maintains Homogeneity // Address address; Contact contact1, contact2; Pair contacts From the Library of Wow! eBook 458 Chapter 11: Generics // Initialize variables // Error: Cannot convert type IPair pdaPair = (IPair) contacts; pair.First = address; Thus, casting Pair to IPair... more specific For example, the following will cause a compile error: Pair contacts = (IPair) pdaPair; Doing so would cause a similar problem to covariance Items within pdaPair could potentially be heterogeneous (addresses and contacts) and constraining to all contacts would be invalid Enabling Covariance with the out Type Parameter Modifier in C# 4.0 It is important to note that you... public static T Min(T first, params T[] values) where T : IComparable { T minimum = first; foreach (T item in values) { if (item.CompareTo(minimum) < 0) { minimum = item; } } From the Library of Wow! eBook 454 Chapter 11: Generics return minimum; } } You use the same syntax on a generic class when the method requires an additional type parameter not included in the class type parameter list In this... Using the out Type Parameter Modifier // interface IReadOnlyPair { T First { get; } T Second { get; } } interface IPair { T First { get; set; } T Second { get; set; } } From the Library of Wow! eBook 460 Chapter 11: Generics public struct Pair : IPair, IReadOnlyPair { // } class Program { static void Main() { // Allowed in C# 4.0 Pair contacts = new Pair( new Contact("Princess... will allow any covariant assignments to the interface Enabling Contravariance with the in Type Parameter Modifier in C# 4.0 As I mentioned earlier, contravariance is also invalid A Pair could potentially be heterogeneous (containing both an Address and a Contact), and constraining it to only be Contacts when it contains Addresses would invalid However, imagine an IWriteOnlyPair (see Listing... would also not affect the validity of what the IWriteOnlyPair allowed Listing 11.44: Covariance Using the out Type Parameter Modifier interface IReadOnlyPair { // } From the Library of Wow! eBook Covariance and Contravariance 461 interface IWriteOnlyPair { T First { set; } T Second { set; } } interface IPair { T First { get; set; } T Second { get; set; } } public struct Pair : . 11 .5. Listing 11.37: Inferring the Type Parameter Console.WriteLine( MathEx.Max(7, 49 0) ); Console.WriteLine( MathEx.Min("R.O.U.S'", "Fireswamp")); OUTPUT 11 .4: 49 0. Show(tree.SubItems.Second, indent+1); } } OUTPUT 11 .5: 49 0 Fireswamp where T : IComparable<T> From the Library of Wow! eBook ptg Chapter 11: Generics 456 Notice that the Show<T> implementation. in Listing 11 . 45 . { T First { set; } T Second { set; } IWriteOnlyPair<Contact> pair = contacts; From the Library of Wow! eBook ptg Chapter 11: Generics462 Listing 11 . 45 : Covariance Using

Ngày đăng: 18/06/2014, 17:20

Từ khóa liên quan

Mục lục

  • Contents

  • Contents of C# 4.0 Topics

  • Figures

  • Tables

  • Foreword

  • Preface

  • Acknowledgments

  • About the Author

  • 1 Introducing C#

    • Hello, World

      • Compiling and Running the Application

      • C# Syntax Fundamentals

        • Type Definition

        • Main

        • Statements and Statement Delimiters

        • Whitespace

        • Working with Variables

          • Data Types

          • Declaring a Variable

          • Assigning a Variable

          • Using a Variable

          • Console Input and Output

            • Getting Input from the Console

            • Writing Output to the Console

            • Comments

Tài liệu cùng người dùng

Tài liệu liên quan