Apress Introducing Dot Net 4 With Visual Studio_5 potx

59 441 0
Apress Introducing Dot Net 4 With Visual Studio_5 potx

Đ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

CHAPTER 11 ■ GENERICS 325 Constructed Types Control Accessibility When you build constructed types from generic types, you must consider the accessibility of both the generic type and the types provided as the type arguments, in order to determine the accessibility of the whole constructed type. For example, the following code is invalid and will not compile: public class Outer { private class Nested { } public class GenericNested<T> { } private GenericNested<Nested> field1; public GenericNested<Nested> field2; // Ooops! } The problem is with field2. The Nested type is private, so how can GenericNested<Nested> possibly be public? Of course, the answer is that it cannot. With constructed types, the accessibility is an intersection of the accessibility of the generic type and the types provided in the argument list. Generics and Inheritance C# generic types cannot directly derive from a type parameter. However, you can use the following type parameters to construct the base types they do derive from: // This is invalid!! public class MyClass<T> : T { } // But this is valid. public class MyClass<T> : Stack<T> { } ■ Tip With C++ templates, deriving directly from a type parameter provides a special flexibility. If you’ve ever used the Active Template Library (ATL) to do COM development, you have no doubt come across this technique because ATL employs it extensively to avoid the need for virtual method calls. The same technique is used with C++ templates to generate entire hierarchies at compile time. For more examples, I suggest you read Andrei Alexandrescu’s Modern C++ Design: Generic Programming and Design Patterns Applied (Boston, MA: Addison- CHAPTER 11 ■ GENERICS 326 Wesley Professional, 2001). This is yet another example showing how C++ templates are static in nature, whereas C# generics are dynamic. Let’s examine techniques that you can use to emulate the same behavior to some degree. As is often the case, you can add one more level of indirection to achieve something similar. In C++, when a template type derives directly from one of the type arguments, it is often assumed that the type specified for the type argument exhibits a certain desired behavior. For example, you can do the following using C++ templates: // NOTE: This is C++ code used for the sake of example class Employee { public: long get_salary() { return salary; } void set_salary( long salary ) { this->salary = salary; } private: long salary; }; template< class T > class MyClass : public T { }; void main() { MyClass<Employee> myInstance; myInstance.get_salary(); } In the main function, pay attention to the call to get_salary. Even though it looks odd at first, it works just fine because MyClass<T> inherits the implementation of whatever type is specified for T at compile time. In this case, that type, Employee, implements get_salary, and MyClass<Employee> inherits that implementation. Clearly, an assumption is being placed on the type that is provided for T in MyClass<T> that the type will support a method named get_salary. If it does not, the C++ compiler will complain at compile time. This is a form of static polymorphism or policy-based programming. In traditional cases, polymorphism is explained within the context of virtual methods known as dynamic polymorphism. You cannot implement static polymorphism with C# generics. However, you can require that the type arguments given when forming a closed type support a specific contract by using a mechanism called constraints, which I cover in the following section. CHAPTER 11 ■ GENERICS 327 Constraints So far, the majority of generics examples that I’ve shown involve some sort of collection-style class that holds a bunch of objects or values of a specific type. But you’ll often need to create generic types that not only contain instances of various types but also use those objects directly by calling methods or accessing properties on them. For example, suppose that you have a generic type that holds instances of arbitrary geometric shapes that all implement a property named Area. Also, you need the generic type to implement a property—say, TotalArea—in which all the areas of the contained shapes are accumulated. The guarantee here is that each geometric shape in the generic container will implement the Area property. You might be inclined to write code like the following: using System; using System.Collections.Generic; public interface IShape { double Area { get; } } public class Circle : IShape { public Circle( double radius ) { this.radius = radius; } public double Area { get { return 3.1415*radius*radius; } } private double radius; } public class Rect : IShape { public Rect( double width, double height ) { this.width = width; this.height = height; } public double Area { get { return width*height; } } private double width; private double height; } CHAPTER 11 ■ GENERICS 328 public class Shapes<T> { public double TotalArea { get { double acc = 0; foreach( T shape in shapes ) { // THIS WON'T COMPILE!!! acc += shape.Area; } return acc; } } public void Add( T shape ) { shapes.Add( shape ); } private List<T> shapes = new List<T>(); } public class EntryPoint { static void Main() { Shapes<IShape> shapes = new Shapes<IShape>(); shapes.Add( new Circle(2) ); shapes.Add( new Rect(3, 5) ); Console.WriteLine( "Total Area: {0}", shapes.TotalArea ); } } There is one major problem, as the code won’t compile. The offending line of code is inside the TotalArea property of Shapes<T>. The compiler complains with the following error: error CS0117: 'T' does not contain a definition for 'Area' All this talk of requiring the contained type T to support the Area property sounds a lot like a contract because it is! C# generics are dynamic as opposed to static in nature, so you cannot achieve the desired effect without some extra information. Whenever you hear the word contract within the C# world, you might start thinking about interfaces. Therefore, I chose to have both of my shapes implement the IShape interface. Thus, the IShape interface defines the contract, and the shapes implement that contract. However, that still is not enough for the C# compiler to be able to compile the previous code. C# generics must have a way to enforce the rule that the type T supports a specific contract at runtime. A naïve attempt to solve the problem could look like the following: public class Shapes<T> { CHAPTER 11 ■ GENERICS 329 public double TotalArea { get { double acc = 0; foreach( T shape in shapes ) { // DON'T DO THIS!!! IShape theShape = (IShape) shape; acc += theShape.Area; } return acc; } } public void Add( T shape ) { shapes.Add( shape ); } private List<T> shapes = new List<T>(); } This modification to Shapes<T> indeed does compile and work most of the time. However, this generic has lost some of its innocence due to the type cast within the foreach loop. Just imagine that if during a late-night caffeine-induced trance, you attempted to create a constructed type Shapes<int>. The compiler would happily oblige. But what would happen if you tried to get the TotalArea property from a Shapes<int> instance? As expected, you would be treated to a runtime exception as the TotalArea property accessor attempted to cast an int into an IShape. One of the primary benefits of using generics is better type safety, but in this example I tossed type safety right out the window. So, what are you supposed to do? The answer lies in a concept called generic constraints. Check out the following correct implementation: public class Shapes<T> where T: IShape { public double TotalArea { get { double acc = 0; foreach( T shape in shapes ) { acc += shape.Area; } return acc; } } public void Add( T shape ) { shapes.Add( shape ); } private List<T> shapes = new List<T>(); } Notice the extra line under the first line of the class declaration using the where keyword. This says, “Define class Shapes<T> where T must implement IShape.” Now the compiler has everything it needs to enforce type safety, and the JIT compiler has everything it needs to build working code at runtime. The CHAPTER 11 ■ GENERICS 330 compiler has been given a hint to help it notify you, with a compile-time error, when you attempt to create constructed types where T does not implement IShape. The syntax for constraints is pretty simple. There can be one where clause for each type parameter. Any number of constraints can be listed following the type parameter in the where clause. However, only one constraint can name a class type (because the CLR has no concept of multiple inheritance), so that constraint is known as the primary constraint. Additionally, instead of specifying a class name, the primary constraint can list the special words class or struct, which are used to indicate that the type parameter must be any class or any struct. The constraint clause can then include as many secondary constraints as possible, such as a list of interfaces that the parameterized type must implement. Finally, you can list a constructor constraint that takes the form new() at the end of the constraint list. This constrains the parameterized type so it is required to have a default parameterless constructor. Class types must have an explicitly defined default constructor to satisfy this constraint, whereas value types have a system-generated default constructor. It is customary to list each where clause on a separate line in any order under the class header. A comma separates each constraint following the colon in the where clause. That said, let’s take a look at some constraint examples: using System.Collections.Generic; public class MyValueList<T> where T: struct // But can't do the following // where T: struct, new() { public void Add( T v ) { imp.Add( v ); } private List<T> imp = new List<T>(); } public class EntryPoint { static void Main() { MyValueList<int> intList = new MyValueList<int>(); intList.Add( 123 ); // CAN'T DO THIS. // MyValueList<object> objList = // new MyValueList<object>(); } } In this code, you can see an example of the struct constraint in the declaration for a container that can contain only value types. The constraint prevents one from declaring the objList variable that I have commented out in this example because the result of attempting to compile it presents the following error: CHAPTER 11 ■ GENERICS 331 error CS0453: The type 'object' must be a non-nullable value type in order  to use it as parameter 'T' in the generic type or method 'MyValueList<T>' Alternatively, the constraint could have also claimed to allow only class types. Incidentally, in the Visual Studio version of the C# compiler, I can’t create a constraint that includes both class and struct. Of course, doing so is pointless because the same effect comes from including neither struct nor class in the constraints list. Nevertheless, the compiler complains with an error if you try to do so, claiming the following: error CS0449: The 'class' or 'struct' constraint must come before any  other constraints This looks like the compiler error could be better stated by saying that only one primary constraint is allowed in a constraint clause. You’ll also see that I commented out an alternate constraint line, in which I attempted to include the new() constraint to force the type given for T to support a default constructor. Clearly, for value types, this constraint is redundant and should be harmless to specify. Even so, the compiler won’t allow you to provide the new() constraint together with the struct constraint. Now let’s look at a slightly more complex example that shows two constraint clauses: using System; using System.Collections.Generic; public interface IValue { // IValue methods. } public class MyDictionary<TKey, TValue> where TKey: struct, IComparable<TKey> where TValue: IValue, new() { public void Add( TKey key, TValue val ) { imp.Add( key, val ); } private Dictionary<TKey, TValue> imp = new Dictionary<TKey, TValue>(); } I declared MyDictionary<TKey, TValue> so that the key value is constrained to value types. I also want those key values to be comparable, so I’ve required the TKey type to implement IComparable<TKey>. This example shows two constraint clauses, one for each type parameter. In this case, I’m allowing the TValue type to be either a struct or a class, but I do require that it support the defined IValue interface as well as a default constructor. Overall, the constraint mechanism built into C# generics is simple and straightforward. The complexity of constraints is easy to manage and decipher with few if any surprises. As the language and the CLR evolve, I suspect that this area will see some additions as more and more applications for generics are explored. For example, the ability to use the class and struct constraints within a constraint clause was a relatively late addition to the standard. CHAPTER 11 ■ GENERICS 332 Finally, the format for constraints on generic interfaces is identical to that of generic classes and structs. Constraints on Nonclass Types So far, I’ve discussed constraints within the context of classes, structs, and interfaces. In reality, any entity that you can declare generically is capable of having an optional constraints clause. For generic method and delegate declarations, the constraints clauses follow the formal parameter list to the method or delegate. Using constraint clauses with method and delegate declarations does provide for some odd-looking syntax, as shown in the following example: using System; public delegate R Operation<T1, T2, R>( T1 val1, T2 val2 ) where T1: struct where T2: struct where R: struct; public class EntryPoint { public static double Add( int val1, float val2 ) { return val1 + val2; } static void Main() { var op = new Operation<int, float, double>( EntryPoint.Add ); Console.WriteLine( "{0} + {1} = {2}", 1, 3.2, op(1, 3.2f) ); } } I declared a generic delegate for an operator method that accepts two parameters and has a return value. My constraint is that the parameters and the return value all must be value types. Similarly, for generic methods, the constraints clauses follow the method declaration but precede the method body. Co- and Contravariance Variance is all about convertibility and being able to do what makes type-sense. For example, consider the following code, which demonstrates array covariance that has been possible in C# since the 1.0 days: using System; static class EntryPoint { static void Main() { string[] strings = new string[] { "One", CHAPTER 11 ■ GENERICS 333 "Two", "Three" }; DisplayStrings( strings ); // Array covariance rules allow the following // assignment object[] objects = strings; // But what happens now? objects[1] = new object(); DisplayStrings( strings ); } static void DisplayStrings( string[] strings ) { Console.WriteLine( " Printing strings " ); foreach( var s in strings ) { Console.WriteLine( s ); } } } At the beginning of the Main method, I create an array of strings and then immediately pass it to DisplayStrings to print them to the console. Then, I assign a variable of type objects[] from the variable strings. After all, because strings and objects are reference type variables, at first glance it makes logical sense to be able to assign strings to objects because a string is implicitly convertible to an object. However, notice right after doing so, I modify slot one and replace it with an object instance. What happens when I call DisplayStrings the second time passing the strings array? As you might expect, the runtime throws an exception of type ArrayTypeMismatchException shown as follows: Unhandled Exception: System.ArrayTypeMismatchException: Attempted to access an element as a type incompatible with the array. Array covariance in C# has been in the language since the beginning for Java compatibility. But because it is flawed, and some say broken, then how can we fix this problem? There are a few ways indeed. Those of you familiar with functional programming will naturally suggest invariance as the solution. That is, if an array is invariant similar to System.String, a copy is made typically in a lazy fashion at the point where one is assigned into another variable. However, let’s see how we might fix this problem using generics: using System; using System.Collections.Generic; static class EntryPoint { static void Main() { List<string> strings = new List<string> { "One", "Two", "Three" CHAPTER 11 ■ GENERICS 334 }; // THIS WILL NOT COMPILE!!! List<object> objects = strings; } } The spirit of the preceding code is identical to the array covariance example, but it will not compile. If you attempt to compile this, you will get the following compiler error: error CS0029: Cannot implicitly convert type  'System.Collections.Generic.List<string>' to  'System.Collections.Generic.List<object>' The ultimate problem is that each constructed type is an individual type, and even though they might originate from the same generic type, they have no implicit type relation between them. For example, there is no implicit relationship between List<string> and List<object>, and just because they both are constructed types of List<T> and string is implicitly convertible to object does not imply that they are convertible from one to the other. Don’t lose hope, though. There is a syntax added in C# 4.0 that allows you to achieve the desired result. Using this new syntax, you can notate a generic interface or delegate indicating whether it supports covariance or contravariance. Additionally, the new variance rules apply only to constructed types in which reference types are passed for the type arguments to the generic type. Covariance Within strongly typed programming languages such as C#, an operation is covariant if it reflects and preserves the ordering of types so they are ordered from more specific types to more generic types. To illustrate, I’ll borrow from the example in the previous section to show how array assignment rules in C# are covariant: string s = "Hello"; object o = s; string[] strings = new string[3]; object[] objects = strings; The first two lines make perfect sense; after all, variables of type string are implicitly convertible to type object because string derives from object. The second set of lines shows that variables of type string[] are implicitly convertible to variables of type object[]. And because the ordering of types between the two implicit assignments is identical that is, from a more specialized type (string) to a more generic type (object) the array assignment operation is said to be covariant. Now, to translate this concept to generic interface assignment, an interface of type IOperation<T> is covariance-convertible to IOperation<R> if there exists an implicit reference conversion from T to R and IOperation<T> to IOperation<R>. Simply put, if for the two conversion operations just mentioned, T and R [...]... { Complex c = new Complex ( 3, 4, EntryPoint.MultiplyInt 64, EntryPoint.AddInt 64, EntryPoint.DoubleToInt 64 ); Console.WriteLine( "Magnitude is {0}", c.Magnitude ); } static Int 64 MultiplyInt 64( Int 64 val1, Int 64 val2 ) { return val1 * val2; } static Int 64 AddInt 64( Int 64 val1, Int 64 val2 ) { return val1 + val2; } static Int 64 DoubleToInt 64( double d ) { return Convert.ToInt 64( d ); } } 350... Main() { Complex c = new Complex ( 3, 4, EntryPoint.MultiplyInt 64, EntryPoint.AddInt 64, EntryPoint.DoubleToInt 64 ); Console.WriteLine( "Magnitude is {0}", c.Magnitude ); } static Int 64 MultiplyInt 64( Int 64 val1, Int 64 val2 ) { return val1 * val2; } static Int 64 AddInt 64( Int 64 val1, Int 64 val2 ) { return val1 + val2; 352 CHAPTER 11 ■ GENERICS } static Int 64 DoubleToInt 64( double d ) { return... Complex c = new Complex ( 3, 4, EntryPoint.MultiplyInt 64, EntryPoint.AddInt 64, EntryPoint.DoubleToInt 64 ); Console.WriteLine( "Magnitude is {0}", c.Magnitude ); } static void DummyMethod( Complex c ) { } static Int 64 AddInt 64( Int 64 val1, Int 64 val2 ) { return val1 + val2; } static Int 64 MultiplyInt 64( Int 64 val1, Int 64 val2 ) { return val1 * val2; } static Int 64 DoubleToInt 64( double... Main() { Complex c = new Complex ( 3, 4, EntryPoint.MultiplyInt 64, EntryPoint.AddInt 64, EntryPoint.Int64ToDouble, EntryPoint.DoubleToInt 64 ); Console.WriteLine( "Magnitude is {0}", c.Magnitude ); } static void DummyMethod( Complex c ) { } static Int 64 MultiplyInt 64( Int 64 val1, Int 64 val2 ) { return val1 * val2; } static Int 64 AddInt 64( Int 64 val1, Int 64 val2 ) { return val1... val1, Int 64 val2 ) { return val1 + val2; 356 CHAPTER 11 ■ GENERICS } static Int 64 DoubleToInt 64( double d ) { return Convert.ToInt 64( d ); } static double Int64ToDouble( Int 64 i ) { return Convert.ToDouble( i ); } } Now, the Complex type can contain any kind of struct, whether it’s generic or not However, you must provide it with the necessary means to be able to convert to and from double as well as... value, and the Func delegates can be used to hold methods that accept up to 16 parameters and do return a value ■ Note Prior to the NET 4. 0 BCL, the Action and Func delegates only accepted up to four parameters Currently, they support up to 16 Starting with NET 4. 0, these generic delegates have also been marked appropriately for variance Thus, the two parameter versions of these will look like... Collections It seems that the most natural use of generics within C# and the CLR is for collection types Maybe that’s because you can gain a huge amount of efficiency when using generic containers to hold value types when compared with the collection types within the System.Collections namespace Of course, you cannot overlook the added type safety that comes with using the generic collections Any time you get... imaginary ) { this.real = real; this.imaginary = imaginary; 347 CHAPTER 11 ■ GENERICS } public T Real { get { return real; } set { real = value; } } public T Img { get { return imaginary; } set { imaginary = value; } } private T real; private T imaginary; } public class EntryPoint { static void Main() { Complex c = new Complex ( 4, 5 ); } } This is a good start, but now let’s make this value... imaginary = value; } } public T Magnitude { get { // WON'T COMPILE!!! return Math.Sqrt( real * real + imaginary * imaginary ); 348 CHAPTER 11 ■ GENERICS } } private T real; private T imaginary; } public class EntryPoint { static void Main() { Complex c = new Complex ( 3, 4 ); Console.WriteLine( "Magnitude is {0}", c.Magnitude ); } } If you attempt to compile this code, you might be surprised... associated with array invariance in C# is avoided by using generics coupled with the variance syntax added to the language in C# 4. 0 In other words, the variance rules for generics are type safe whereas the variance rules for plain old arrays are not Variance and Delegates In general, generic delegates follow the same rules as generic interfaces when applying variance decorations to generic parameters The NET . ■ Note Prior to the .NET 4. 0 BCL, the Action<> and Func<> delegates only accepted up to four parameters. Currently, they support up to 16. Starting with .NET 4. 0, these generic delegates. IEnumerable<T> and IEnumerator<T> types are denoted as covariant with the out keyword starting with the release of C# 4. 0. This is a tremendous help, especially when using LINQ. Contravariance. Therefore, much of the pain associated with array invariance in C# is avoided by using generics coupled with the variance syntax added to the language in C# 4. 0. In other words, the variance rules

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

Từ khóa liên quan

Mục lục

  • Home Page

  • Prelim

  • Contents at a Glance

  • Contents

  • About the Author

  • About the Technical Reviewer

  • Acknowledgments

  • Preface

    • About This Book

  • C# Preview

    • Differences Between C# and C++

      • C#

      • C++

      • CLR Garbage Collection

    • Example of a C# Program

    • Overview of Features Added in C# 2.0

    • Overview of Features Added in C# 3.0

    • Overview of New C# 4.0 Features

    • Summary

  • C# and the CLR

    • The JIT Compiler in the CLR

    • Assemblies and the Assembly Loader

      • Minimizing the Working Set of the Application

      • Naming Assemblies

      • Loading Assemblies

    • Metadata

    • Cross-Language Compatibility

    • Summary

  • C# Syntax Overview

    • C# Is a Strongly Typed Language

    • Expressions

    • Statements and Expressions

    • Types and Variables

      • Value Types

      • Enumerations

      • Flags Enumerations

      • Reference Types

      • Default Variable Initialization

      • Implicitly Typed Local Variables

      • Type Conversion

      • Array Covariance

      • Boxing Conversion

      • as and is Operators

      • Generics

    • Namespaces

      • Defining Namespaces

      • Using Namespaces

    • Control Flow

      • if-else, while, do-while, and for

      • switch

      • foreach

      • break, continue, goto, return, and throw

    • Summary

  • Classes, Structs, and Objects

    • Class Definitions

      • Fields

      • Constructors

      • Methods

      • Static Methods

      • Instance Methods

      • Properties

      • Declaring Properties

      • Accessors

      • Read-Only and Write-Only Properties

      • Auto-Implemented Properties

      • Encapsulation

      • Accessibility

      • Interfaces

      • Inheritance

      • Accessibility of Members

      • Implicit Conversion and a Taste of Polymorphism

      • Member Hiding

      • The base Keyword

      • sealed Classes

      • abstract Classes

      • Nested Classes

      • Indexers

      • partial Classes

      • partial Methods

      • Static Classes

      • Reserved Member Names

      • Reserved Names for Properties

      • Reserved Names for Indexers

      • Reserved Names for Destructors

      • Reserved Names for Events

    • Value Type Definitions

      • Constructors

      • The Meaning of this

      • Finalizers

      • Interfaces

    • Anonymous Types

    • Object Initializers

    • Boxing and Unboxing

      • When Boxing Occurs

      • Efficiency and Confusion

    • System.Object

      • Equality and What It Means

      • The IComparable Interface

    • Creating Objects

      • The new Keyword

      • Using new with Value Types

      • Using new with Class Types

      • Field Initialization

      • Static (Class) Constructors

      • Instance Constructor and Creation Ordering

    • Destroying Objects

      • Finalizers

      • Deterministic Destruction

      • Exception Handling

    • Disposable Objects

      • The IDisposable Interface

      • The using Keyword

    • Method Parameter Types

      • Value Arguments

      • ref Arguments

      • out Parameters

      • param Arrays

      • Method Overloading

      • Optional Arguments

      • Named Arguments

    • Inheritance and Virtual Methods

      • Virtual and Abstract Methods

      • override and new Methods

      • sealed Methods

      • A Final Few Words on C# Virtual Methods

    • Inheritance, Containment, and Delegation

      • Choosing Between Interface and Class Inheritance

      • Delegation and Composition vs. Inheritance

    • Summary

  • Interfaces and Contracts

    • Interfaces Define Types

    • Defining Interfaces

      • What Can Be in an Interface?

      • Interface Inheritance and Member Hiding

    • Implementing Interfaces

      • Implicit Interface Implementation

      • Explicit Interface Implementation

      • Overriding Interface Implementations in Derived Classes

      • Beware of Side Effects of Value Types Implementing Interfaces

    • Interface Member Matching Rules

    • Explicit Interface Implementation with Value Types

    • Versioning Considerations

    • Contracts

      • Contracts Implemented with Classes

      • Interface Contracts

    • Choosing Between Interfaces and Classes

    • Summary

  • Overloading Operators

    • Just Because You Can Doesn’t Mean You Should

    • Types and Formats of Overloaded Operators

    • Operators Shouldn’t Mutate Their Operands

    • Does Parameter Order Matter?

    • Overloading the Addition Operator

    • Operators That Can Be Overloaded

      • Comparison Operators

      • Conversion Operators

      • Boolean Operators

    • Summary

  • Exception Handling and Exception Safety

    • How the CLR Treats Exceptions

    • Mechanics of Handling Exceptions in C#

      • Throwing Exceptions

      • Changes with Unhandled Exceptions Starting with .NET 2.0

      • Syntax Overview of the try, catch, and finally Statements

      • Rethrowing Exceptions and Translating Exceptions

      • Exceptions Thrown in finally Blocks

      • Exceptions Thrown in Finalizers

      • Exceptions Thrown in Static Constructors

    • Who Should Handle Exceptions?

    • Avoid Using Exceptions to Control Flow

    • Achieving Exception Neutrality

      • Basic Structure of Exception-Neutral Code

      • Constrained Execution Regions

      • Critical Finalizers and SafeHandle

    • Creating Custom Exception Classes

    • Working with Allocated Resources and Exceptions

    • Providing Rollback Behavior

    • Summary

  • Working with Strings

    • String Overview

    • String Literals

    • Format Specifiers and Globalization

      • Object.ToString, IFormattable, and CultureInfo

      • Creating and Registering Custom CultureInfo Types

      • Format Strings

      • Console.WriteLine and String.Format

      • Examples of String Formatting in Custom Types

      • ICustomFormatter

      • Comparing Strings

    • Working with Strings from Outside Sources

    • StringBuilder

    • Searching Strings with Regular Expressions

      • Searching with Regular Expressions

      • Searching and Grouping

      • Replacing Text with Regex

      • Regex Creation Options

    • Summary

  • Arrays, Collection Types, and Iterators

    • Introduction to Arrays

      • Implicitly Typed Arrays

      • Type Convertibility and Covariance

      • Sortability and Searchability

      • Synchronization

      • Vectors vs. Arrays

    • Multidimensional Rectangular Arrays

    • Multidimensional Jagged Arrays

    • Collection Types

      • Comparing ICollection<T> with ICollection

      • Collection Synchronization

      • Lists

      • Dictionaries

      • Sets

      • System.Collections.ObjectModel

      • Efficiency

    • IEnumerable<T>, IEnumerator<T>, IEnumerable, and IEnumerator

      • Types That Produce Collections

    • Iterators

      • Forward, Reverse, and Bidirectional Iterators

    • Collection Initializers

    • Summary

  • Delegates, Anonymous Functions, and Events

    • Overview of Delegates

    • Delegate Creation and Use

      • Single Delegate

      • Delegate Chaining

      • Iterating Through Delegate Chains

      • Unbound (Open Instance) Delegates

    • Events

    • Anonymous Methods

      • Captured Variables and Closures

      • Beware the Captured Variable Surprise

      • Anonymous Methods as Delegate Parameter Binders

    • The Strategy Pattern

    • Summary

  • Generics

    • Difference Between Generics and C++ Templates

    • Efficiency and Type Safety of Generics

    • Generic Type Definitions and Constructed Types

      • Generic Classes and Structs

      • Generic Interfaces

      • Generic Methods

      • Generic Delegates

      • Generic Type Conversion

      • Default Value Expression

      • Nullable Types

      • Constructed Types Control Accessibility

      • Generics and Inheritance

    • Constraints

      • Constraints on Nonclass Types

    • Coand Contravariance

      • Covariance

      • Contravariance

      • Invariance

      • Variance and Delegates

    • Generic System Collections

    • Generic System Interfaces

    • Select Problems and Solutions

      • Conversion and Operators within Generic Types

      • Creating Constructed Types Dynamically

    • Summary

  • Threading in C#

    • Threading in C# and .NET

      • Starting Threads

      • Passing Data to New Threads

      • Using ParameterizedThreadStart

      • The IOU Pattern and Asynchronous Method Calls

      • States of a Thread

      • Terminating Threads

      • Halting Threads and Waking Sleeping Threads

      • Waiting for a Thread to Exit

      • Foreground and Background Threads

      • Thread-Local Storage

      • How Unmanaged Threads and COM Apartments Fit In

    • Synchronizing Work Between Threads

      • Lightweight Synchronization with the Interlocked Class

      • SpinLock Class

      • Monitor Class

      • Beware of Boxing

      • Pulse and Wait

      • Locking Objects

      • ReaderWriterLock

      • ReaderWriterLockSlim

      • Mutex

      • Semaphore

      • Events

      • Win32 Synchronization Objects and WaitHandle

    • Using ThreadPool

      • Asynchronous Method Calls

      • Timers

    • Concurrent Programming

      • Task Class

      • Parallel Class

      • Easy Entry to the Thread Pool

    • Thread-Safe Collection Classes

    • Summary

  • In Search of C# Canonical Forms

    • Reference Type Canonical Forms

      • Default to sealed Classes

      • Use the Non-Virtual Interface (NVI) Pattern

      • Is the Object Cloneable?

      • Is the Object Disposable?

      • Does the Object Need a Finalizer?

      • What Does Equality Mean for This Object?

      • Reference Types and Identity Equality

      • Value Equality

      • Overriding Object.Equals for Reference Types

      • If You Override Equals, Override GetHashCode Too

      • Does the Object Support Ordering?

      • Is the Object Formattable?

      • Is the Object Convertible?

      • Prefer Type Safety at All Times

      • Using Immutable Reference Types

    • Value Type Canonical Forms

      • Override Equals for Better Performance

      • Do Values of This Type Support Any Interfaces?

      • Implement Type-Safe Forms of Interface Members and Derived Methods

    • Summary

      • Checklist for Reference Types

      • Checklist for Value Types

  • Extension Methods

    • Introduction to Extension Methods

      • How Does the Compiler Find Extension Methods?

      • Under the Covers

      • Code Readability versus Code Understandability

    • Recommendations for Use

      • Consider Extension Methods Over Inheritance

      • Isolate Extension Methods in Separate Namespace

      • Changing a Type’s Contract Can Break Extension Methods

    • Transforms

    • Operation Chaining

    • Custom Iterators

      • Borrowing from Functional Programming

    • The Visitor Pattern

    • Summary

  • Lambda Expressions

    • Introduction to Lambda Expressions

      • Lambda Expressions and Closures

      • Closures in C# 1.0

      • Closures in C# 2.0

      • Lambda Statements

    • Expression Trees

      • Operating on Expressions

      • Functions as Data

    • Useful Applications of Lambda Expressions

      • Iterators and Generators Revisited

      • More on Closures (Variable Capture) and Memoization

      • Currying

      • Anonymous Recursion

    • Summary

  • LINQ: Language Integrated Query

    • A Bridge to Data

      • Query Expressions

      • Extension Methods and Lambda Expressions Revisited

    • Standard Query Operators

    • C# Query Keywords

      • The from Clause and Range Variables

      • The join Clause

      • The where Clause and Filters

      • The orderby Clause

      • The select Clause and Projection

      • The let Clause

      • The group Clause

      • The into Clause and Continuations

    • The Virtues of Being Lazy

      • C# Iterators Foster Laziness

      • Subverting Laziness

      • Executing Queries Immediately

      • Expression Trees Revisited

    • Techniques from Functional Programming

      • Custom Standard Query Operators and Lazy Evaluation

      • Replacing foreach Statements

    • Summary

  • Dynamic Types

    • What does dynamic Mean?

    • How Does dynamic Work?

      • The Great Unification

      • Call Sites

      • Objects with Custom Dynamic Behavior

      • Efficiency

      • Boxing with Dynamic

    • Dynamic Conversions

      • Implicit Dynamic Expressions Conversion

    • Dynamic Overload Resolution

    • Dynamic Inheritance

      • You Cannot Derive from dynamic

      • You Cannot Implement dynamic Interfaces

      • You Can Derive From Dynamic Base Types

    • Duck Typing in C#

    • Limitations of dynamic Types

    • ExpandoObject: Creating Objects Dynamically

    • Summary

  • Index

    • Symbols

    • A

    • B

    • C

    • D

    • E

    • F

    • G

    • H

    • I

    • J

    • K

    • L

    • M

    • N

    • O

    • P

    • Q

    • R

    • S

    • T

    • V

    • U

    • W

    • X

    • Y

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

  • Đang cập nhật ...

Tài liệu liên quan