Friday, October 22, 2010

Base respecte des génériques en c#

Translate Request has too much data
Parameter name: request

In C# there is two types of mechanism for writing code that is reusable across different type.
1)Inheritance
2)Generic.

Inheritance express reusability across base type,while generic express reusability with a template that contains placeholder.

Mainly Generics used for type safety and reducing casting and boxing.

Now first glance on Generic Types.

A generic type declares generic parameters—placeholder types to be filled in by the consumer of the generic type, who will supply the generic arguments. Here is a generic type Stack, designed to stack instances of type T. Stack declares a single generic parameter T:


public class Stack
{
int position;
T[] data = new T[100];
public void Push (T obj) { data[position++] = obj; }
public T Pop() { return data[--position]; }
}

We can use Stack as follows:

Stack stack = new Stack ();
stack. Push(5);
stack. Push(10);
int x = stack.Pop(); // x is 10
int y = stack.Pop(); // y is 5

Stack fills in the generic parameter T with the generic argument int, implicitly creating a type on the fly (the synthesis occurs at runtime). Stack effectively has the following definition (substitutions appear in bold, with the class name hashed out to avoid confusion):

public class ###
{
int position;
int[] data;
public void Push (int obj) { data[position++] = obj; }
public int Pop() { return data[--position];}
}

Technically, we say that Stack is an open type, whereas Stack is a closed type. You can only instantiate a closed type, because all the placeholder types must be filled in.

Why generics exist?

Different types of generic code that is reusable for writing exist. Let's say we need a stack of integers, but we did not have normal type. All the necessary elements for a solution (eg, IntStack, StringStack, etc.) Clearly, this type of code duplication would cause quite a different version of the class will hardcode. Another solution is to write a stack object by using the element type as would be generalized:


public class ObjectStack
{
int position;
object[] data = new object[10];
public void Push (object obj){data[position++] = obj;}
public object Pop() { return data[--position];}
}

An ObjectStack, however, wouldn't work as well as a hardcoded IntStack for specifically stacking integers. Specifically, an ObjectStack would require boxing and downcasting that could not be checked at compile time:


// Suppose we just want to store integers here:
ObjectStack stack = new ObjectStack();

stack.Push ("s"); // Wrong type, but no error!
int i = (int)stack.Pop(); // Downcast - runtime error

What we need is both a general implementation of a stack that works for all element types, and a way to easily specialize that stack to a specific element type for increased type safety and reduced casting and boxing. Generics give us precisely this, by allowing us to parameterize the element type. Stack has the benefits of both ObjectStack and IntStack. Like ObjectStack, Stack is written once to work generally across all types. Like IntStack, Stack is specialized for a particular type—the beauty is that this type is T, which we substitute on the fly.

A generic method declares generic parameters within the signature of a method.

With generic methods, many fundamental algorithms can be implemented in only a general-purpose way. Here is a generic method that swaps two values of any type:


static void Swap (ref T a, ref T b)
{
T temp = a; a = b; b = temp;
}

Swap can be used as follows:

int x = 5, y = 10;
Swap (ref x, ref y);

Generally, there is no need to supply type parameters to a generic method, because the compiler can implicitly infer the type. If there is ambiguity, generic methods can be called with the type parameters as follows:


Swap (ref x, ref y);

Within a generic type, a method is not classed as generic unless it introduces generic parameters (with the angle bracket syntax). The Pop method in our generic stack merely uses the type's existing generic parameter, T, and is not classed as a generic method.

Methods and types are the only constructs that can introduce generic parameters. Properties, indexers, events, fields, methods, operators, and so on cannot declare generic parameters, although they can partake in any generic parameters already declared by their enclosing type. In our generic stack example, for instance, we could write an indexer that returns a generic item:


public T this [int index] { get {return data [index];} }

Generic parameters can be introduced in the declaration of classes, structs, interfaces, delegates (see the upcoming "Delegates" section), and methods. Other constructs, such as properties, cannot introduce a generic parameter, but can use a generic parameter. For example, the property Value uses T:


public struct Nullable
{
public T Value {get;}
}

A generic type or method can have multiple parameters. For example:

class Dictionary {...}

To instantiate:

Dictionary myDic = new Dictionary();
var myDic = new Dictionary();

Generic type names and method names can be overloaded as long as the number of generic parameters is different. For example, the following two type names do not conflict:


class A {}
class A {}

typeof and Generics

The typeof operator requires specifying the number of parameters when asking for the type of an open type, as follows:


class A {}
class A {}
...

Type a1 = typeof(A<>);
Type a2 = typeof(A<,>);

Here is an example of asking for the type of a closed type:

Type a3 = typeof(A);

The default keyword can be used to get the default value given a generic type argument. The default value for a reference type is null, and the default value for a value type is the result of bitwise-zeroing the value type's fields:

static void Zap (T[] array)
{
for (int i = 0; i < array.Length; i++)
array[i] = default(T);
}

By default, a generic parameter can be substituted with any type whatsoever. Constraints can be applied to a generic parameter to require more specific type arguments. These are the possible constraints:

where T : base-class // Base class constraint
where T : interface // Interface constraint
where T: class // Class constraint
where T : struct // Struct constraint
where T : new() // Parameterless constructor
// constraint
where U : T // Naked type constraint

In the following example, GenericClass requires T to derive from SomeClass and implement Interface1:


class SomeClass {}
interface Interface1 {}

class GenericClass where T : SomeClass, Interface1 {}

Constraints can be applied wherever generic parameters are defined, in both methods and type definitions.

A base class constaint or interface constraint specifies that the type parameter must subclass or implement a particular class or interface. This allows instances of that type to be implicitly cast to that class or interface. For example, suppose we want to write a generic Max method that returns the maximum of two values. We can take advantage of the generic interface defined in the System namespace IComparable:


public interface IComparable
{
int CompareTo (T other);
}

CompareTo returns a positive number if other is greater than this. Using this interface as a constraint, we can write a Max method as follows (to avoid distraction, null checking is omitted):


static T Max (T a, T b) where T : IComparable
{
return a.CompareTo (b) > 0 ? a : b;
}

The Max method can accept arguments of any type implementing IComparable (which includes most built-in types such as int and string):

int z = Max (5, 10); // 10
string last = Max ("ant", "zoo"); // zoo

The class constraint and struct constraint simply specify that T must be a class or a struct. A great example of the struct constraint is the System.Nullable struct (we will discuss this class in depth, later, in the "Nullable Types" section):

struct Nullable where T : struct {...}

The parameterless constructor constraint requires T to have a public parameterless constructor. If this constraint is defined, you can call new() on T:


static void Initialize (T[] array) where T : new()
{
for (int i = 0; i < array.Length; i++)
array[i] = new T();
}

The naked type constraint requires one generic parameter to derive from another generic parameter. In this example, the method FilteredStack returns another Stack, containing only the subset of elements where the generic parameter T is of the generic parameter U:

class Stack
{
Stack FilteredStack() where U : T {...}
}

Generic types are not covariant. This means that even if B can be cast to A, T cannot be cast to T. For example, suppose Animal and Bear are defined as follows:

class Animal {}
class Bear : Animal {}

The following is illegal:

Stack bears = new Stack ();

// compile-time error
Stack animals = bears;

Lack of covariance can hinder reusability. Suppose, for instance, we wanted to write a method to Wash a stack of animals:


public class ZooCleaner
{
public static void Wash (Stack animals) {...}
}

Calling Wash with a stack of bears would generate a compile-time error. The workaround is to redefine the Wash method with a constraint:


public class ZooCleaner
{
public static void Wash (Stack animals)
where T : Animal {}
}

We can now call Wash as follows:

Stack bears = new Stack();
ZooCleaner.Wash (bears);


A generic class can be subclassed just like a nongeneric class. The subclass can leave the base class's generic parameters open, as in the following example:
class Stack {...}
class SpecialStack : Stack {...}

Or the subclass can close the generic type parameters with a concrete type:

class IntStack : Stack { ... }

A subclass can also introduce fresh generic arguments:

class Single { ... }
class Double : Single { ... }


A type can name itself as the concrete type when closing a generic argument:
public interface IEquatable { bool Equals (T obj); }

public class Balloon : IEquatable
{
string color;
int cc;

public bool Equals (Balloon b)
{
if (b == null) return false;
return b.color == color && b.cc == cc;
}
}

Static data is unique for each closed type:


class Bob { public static int Count; }

class Test
{
static void Main()
{
Console.WriteLine (++Bob.Count); // 1
Console.WriteLine (++Bob.Count); // 2
Console.WriteLine (++Bob.Count); // 1
Console.WriteLine (++Bob.Count); // 1
}
}
}

You can instantiate and populate a generic collection in a single step, as follows:

using System.Collections.Generic;
...
List list = new List {1, 2, 3};

The compiler translates this to:

using System.Collections.Generic;
...

List list = new List();
list.Add (1); list.Add (2); list.Add (3);

This requires that the collection implements the ICollection interface, defined in System.Collections.Generic—the standard .NET interface for mutable collections.

No comments:

Post a Comment