Home  >  Article  >  Backend Development  >  Overview and specific uses of generics

Overview and specific uses of generics

零下一度
零下一度Original
2017-06-23 16:32:421495browse

1. Overview of generics

Generic classes and generic methods combine reusability, type safetyandhigh efficiency In one body, it is beyond the reach of the corresponding non-generic classes and methods. Generics are widely used in containers (collections) and methods that operate on containers. The .NET Framework 2.0 class library provides a new namespace System.Collections.Generic, which contains some new generic-based container classes.

  1. Variable type parameters of generics: usually T, but any non-keywords and reserved words can also be used;

  2. All variable types T are in the form of placeholders at compile time, and all dot symbols will be replaced by the actual type passed in at runtime;

2. Advantages of generics

For early versions of common language runtime and C# language limitations, generics provide a solution. The generalization of previous types was achieved by converting the type to the global base class System.Object. The ArrayList container class of the .NET Framework basic class library is an example of this limitation. ArrayList is a very convenient container class that can store any reference type or value type without changing it during use.

##ArrayList list = new ArrayList();

list.Add(1);
list.Add(175.50);
list.Add("hello kitty");

double sum = 0;


foreach(int value in list){
sum += value;
}

Disadvantages:

Convenience comes at a price, which requires adding any reference type or value type to ArrayList are implicitly upcast to System.Object. If the elements are value types, they must be boxed when added to the list and unboxed when retrieved. Type conversions and boxing and unboxing operations all reduce performance; the impact of boxing and unboxing can be significant when large containers must be iterated. Another limitation is the lack of compile-time type checking. When an ArrayList converts any type to Object, it cannot prevent errors like sum+=vlaue in customer code at compile time;

In System In the generic List container in the .Collections.Generic namespace, the same operation is performed to add elements to the container, similar to this:

List listInt = new List();

listInt.Add(100);
listInt.Add(200);
listInt.Add(123.112); //Compilation error
listInt.Add("heel"); //Compilation error

double sum = 0;

foreach (int value in list)
{
sum += value;
}

Compared with ArrayList, the only additional List syntax in client code is the type in declaration and instantiation parameter. The reward for slightly more complex code is that the table you create is not only safer than an ArrayList, but also significantly faster, especially when the elements in the table are value types.

3. Generic type parameters

In the definition of a generic type or generic method, the type parameter is a placeholder, usually a Uppercase letters (you can also use any non-keyword and reserved word names), such as T. When the client code declares and instantiates a variable of this type, replace T with the data type specified by the client code. A generic class, such as the List class given in Generics, cannot be used as-is because it is not a true type but more like a blueprint of a type. To use MyList, client code must declare and instantiate a constructed type by specifying a type parameter within angle brackets. The type parameters of this particular class can be of any type recognized by the compiler. You can create any number of instances of a constructed type, each using different type parameters, as follows:

##List listInt = new List< int>();

4. Constraints on generic type parameters

Generics provide the following five constraints:

List listFloat = new List();

List listString = new List();


Constraint Description
where T : struct The parameter type must be a value type
where T : class The parameter type must be a reference type
where T : new() The parameter type must be There is a public parameterless constructor. When used in conjunction with other constraints, the new() constraint must be placed last.
where T : The parameter type must be the specified base type or a subclass derived from the specified base type
where T : The parameter type must be the specified interface or the implementation of the specified interface. Constraints can be specified for multiple interfaces. Interface constraints can also be generic.

Unrestricted type parameters:

  1. cannot be used!= and == can be used Variable type instances are compared because there is no guarantee that specific type parameters support these operators;

  2. They can be converted to and from System.Object or explicitly Can be converted to any interface type;

  3. can be compared with null. If an unqualified type parameter is compared with null, the result of the comparison is always false when the type parameter is a value type.

No type constraints: When the constraint is a generic type parameter, it is called Naked type constraints.

In the above example, T in the context of the Add method is an untyped constraint; while T in the context of the List class is an unlimited type parameter.

Untyped constraints can also be used in the definition of generic classes. Note that untyped constraints must also be declared in angle brackets together with other type parameters:

//naked type constraint

##public class MyClass where T : V

Because the compiler only thinks that untyped constraints are inherited from System.Object, generics with untyped constraints Classes have very limited uses. Use an untyped constraint on a generic class when you want to enforce an inheritance relationship between two type parameters.

5. Generic classes

Generic classes encapsulate operations that are not specific to any specific data type. Generic classes are often used in container classes, such as linked lists, hash tables, stacks, queues, trees, etc. Operations in these classes, such as adding and removing elements from the container, perform almost the same operations regardless of the type of data stored.

Typically, create a generic class from an existing concrete class and change the type to type parameters one at a time until the best balance of generality and usability is achieved. When creating your own generic classes, important things to consider are:

  • Which types should be generalized to type parameters. The general rule is that the more types represented by parameters, the greater the flexibility and reusability of the code. Too much generalization can make the code difficult to understand by other developers.

  • #If there are constraints, what constraints are required for the type parameters. A good practice is to use the largest constraints possible while ensuring that all types that need to be processed can be processed. For example, if you know that your generic class is only going to use reference types, then apply the constraints for that class. This prevents inadvertent use of value types, and you can use the as operator on T and check for null references;

  • Put generic behavior in the base class Or in a subcategory. Generic classes can be used as base classes. This should also be considered in the design of non-generic classes. Inheritance rules for generic base classes;

  • Whether to implement one or more generic interfaces. For example, to design a class that creates elements in a generics-based container, you might want to implement an interface like IComparable, where T is a parameter to the class.

##For a generic class Node, client code can either specify a type parameter to create a closed constructed type (Node), or Retainable type parameters are not specified, such as specifying a generic base class to create an open constructed type (Node). Generic classes can inherit from concrete classes, closed constructed types, or open constructed types:

##class List

{
void Add(List items) where U : T
{
}
}

##// concrete typeclass Node : BaseNodeNon-generic concrete classes can inherit from closed constructor base classes, but cannot inherit from open constructor base classes. This is because the client code cannot provide the type parameters required by the base class:
//closed constructed type

class Node : BaseNode
//open constructed type
class Node : BaseNode


//No error.class Node : BaseNode//Generates an error.

class Node : BaseNode


##Generic concrete classes can inherit from open Construct type. Except for type parameters shared with subclasses, the type must be specified for all type parameters:

//Generates an error.class Node : BaseNode {…}//Okay. Generic classes that inherit from open structure types must specify parameter types and constraints:
class Node : BaseNode {…}





class NodeItem where T : IComparable, new() {…}class MyNodeItem : NodeItem where T : IComparable, new() {…} Generic types can use a variety of type parameters and constraints:



class KeyType {…}class SuperKeyType where U : IComparable, where V : new() {…}



Open and closed constructed types can be used as parameters to methods:

#void Swap(List list1, List list2) {…}

void Swap(List list1, List list2) {…}

##6. Generic interface

When an interface is specified as a constraint for a type parameter, only the type that implements the interface can be used as a type parameter.

You can specify multiple interfaces as constraints in one type, as follows:

class Stack, IMyStack1{}##An interface can define multiple type parameters, as follows:

IDictionary##Inheritance of interfaces and classes The rules are the same:

##//Okay.IMyInterface: IBaseInterface//Okay.IMyInterfaceConcrete classes can implement closed construction interfaces, as follows:
//Okay.

IMyInterface: IBaseInterface
//Error.
IMyInterface : IBaseInterface2





class MyClass : IBaseInterface

##A generic class can implement a generic interface or a closed constructed interface, as long as the class's parameter list provides the interface All required parameters are as follows:

##//Okay.class MyClass : IBaseInterface

// Okay.class MyClass : IBaseInterface



Generic class, generic structure, and generic interface are all Rules with the same method overloading.

7. Generic methods

Generic methods are methods that declare type parameters, as follows:

##void Swap(ref T lhs, ref T rhs)

{ T temp; temp = lhs;

lhs = rhs; rhs = temp;}






The following example code shows a Example of calling a method using int as a type parameter:

int a = 1;

int b = 2;// …Swap(a, b);

Swap(a, b);


##You can also ignore the type parameter and the compiler will infer it . The following code to call Swap is equivalent to the above example:


Static methods and instance methods have the same type inference rules. The compiler is able to infer type parameters based on the method parameters passed in; it cannot be determined based on constraints or return values ​​alone. Therefore type inference is invalid for methods without parameters. Type inference occurs at compile time, before the compiler resolves overloaded method flags. The compiler applies type inference logic to all generic methods with the same name. During the overload resolution phase, the compiler includes only those generic classes for which type inference was successful.
In a generic class, non-generic methods can access type parameters in the class:

class List

{

   void Swap(ref T lhs, ref T rhs) { ... }}

##class List{ void Swap(ref T lhs, ref T rhs) { }

##In a generic class, define a generic method with the same type parameters as the class in which it is located; if you try to do this, the compiler will generate warning CS0693.



}

warning CS0693: Type parameter 'T' has the same name as a type parameter in external type 'List'

##

In a generic class, define a generic method to define an undefined type parameter in the generic class: (not commonly used, generally used with constraints)

class List
{
void Swap(ref T lhs, ref T rhs) { } //Not commonly used

void Add(List items) where U : T{} //Commonly used
}

##Generic methods pass multiple type parameters to overload. For example, the following methods can be placed in the same class:

## 8. Generics default keyword
void DoSomething() { }

void DoSomething( ) { }
void DoSomething() { }

One problem that will arise in generic classes and generic methods is how to assign default values ​​​​to parameterized types. At this time, the following two points cannot be known in advance:

Will T be a value type or a reference type
  1. If T is a value type, then T Will it be a numeric value or a structure
  2. For a variable t of parameterized type T, the t = null statement is legal only when T is a reference type; t = 0 is only valid for numerical values, not for structures. The solution to this problem is to use the default keyword, which returns null for reference types and zero for value types. For structures, it will return each member of the structure and, depending on whether the member is a value type or a reference type, returns zero or null.

##class GenericClass

{ T GetElement() { Return default(T);
}
}



The above is the detailed content of Overview and specific uses of generics. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn