Generic
What is generic
The generic feature provides a more elegant way to allow multiple types to share a set of code. Allow us to declare generic parameters of the type (type-parameterized) code, it may be instances of different types. In other words, we can use "type placeholders" to write code, and then specify the real type when creating an instance of the class
Generics in C#
- C# provides 5 generic types: class, structure, interface, delegate and method.The first four are types and methods are members
Generic types are templates for types
Generic and user-defined types
Generic class
Generic classes are not actual classes, but class templates , so we must first construct the actual class types from them, and then create an instance of this constructed class type
- Use placeholders on certain types to declare a class
- Provide the real type for the placeholder. In this way, there is a definition of the real class, which fills all the "vacancies". This type is called a constructed type (constructedtype)
- Create instances of constructed types
Create an instance from a generic type
Declare a generic class
Declaring a simple generic class is similar to declaring a normal class
The differences are as follows:
- Put a set of angle brackets after the class name
- Placeholder strings separated by commas in angle brackets to indicate the desired type. This is called type parameter (typcparameter)
- Use type parameters in the body of a generic class declaration to indicate the type that should be replaced
// 类型参数
class SomeClass < T1, T2 >
{
// T1、T2表示在当前位置使用类型
public T1 SomeVar = new T1();
public T2 OtherVar = new T2();
}
Create construction type
Once the generic type is created, we need to tell the compiler which real types can be used in place of placeholders (type parameters). The compiler obtains these real types and creates a constructed type (a template used to create a real class object)
. The syntax for creating a constructed type is as follows, including listing the class name and providing the real type in angle brackets instead of type parameters. The real type to replace the type parameter is called type argument (typeargument) The difference between
SomeClass < short, int >
type parameter and type argument (below)
Create variables and instances
MyNonGenClass myNGC = new MyNonGenClass ();
// 构造类 构造类
SomeClass < short, int > mySc1 = new SomeClass < short, int >();
var mySc2 = new SomeClass < short, int >();
ps:Many different class types can be constructed from the same generic class. Each has a separate class type, as if they all have independent non-generic declarations
The difference between non-generic stack and generic stack
- | Non-generic | Generic |
---|---|---|
Source code size | Bigger: we need to write a new implementation for each type | Smaller: Regardless of the number of construction types, we only need one implementation |
Executable size | Regardless of whether each version of the stack will be used, it will appear in the compiled version | Only types with structural types appear in executable files |
Ease of writing | Easy to write because it is more specific | More difficult to write because it is more abstract |
Ease of maintenance | More prone to problems, because all modifications need to be applied to every available type | Easy to maintain, because only one place needs to be modified |
Type parameter constraints
Constraints are listed using the where clause
- Each constrained type parameter has its own where clause
- If the parameter has multiple constraints, they are separated by commas in the where clause
where 参数类型 : 约束1, 约束2, ...
The main points of the where clause:
- They are listed after the closing angle bracket in the type parameter list
- They are not separated by commas or other symbols
- They can be listed in any order
- where is a contextual keyword, so it can be used in other contexts
Constraint type and order
Class name | Only classes of this type or classes inherited from it can be used as type arguments |
---|---|
class | Any reference type, including classes, arrays, delegates and interfaces can be used as type arguments |
struct | Any value type can be used as a type argument |
Interface name | Only this interface or the type that implements this interface can be used as a type argument |
new() | Any type with a public constructor without parameters can be used as type arguments. This is called a constructor constraint |
ps: The where clause can be listed in any order. However, the constraints in the where clause must have a specific order
- There can be at most one primary constraint, if there is one, it must be placed first
- There can be any number of interface name constraints
- If there is a constructor constraint, it must be placed last
Generic method
Unlike other generics, methods are members, not types. Generic methods may be generic and non-generic structures and interface declarations
statements generic method
with an optional type parameter list and constraint generic method
- The generic method has two parameter lists
- List of method parameters enclosed in parentheses
- List of type parameters enclosed in angle brackets
- To declare a generic method, you need:
- Put the type parameter list after the method name and before the method parameters
- Place optional constraint clauses after the method parameter list
eg:
public void PrintData <S, T>(string str, int t) where S:Person
{
...
}
Calling a generic method
To call a generic method, the type argument should be provided when the method is called
MyMethod <short,int>();
Examples of generic methods
class Simple // 非泛型类
{
static public void ReverseAndPrint<T>(T[] arr) // 泛型方法
{
Array.Reverse(arr);
foreach (T iten in arr) // 使用类型参T
Console.krite("(0),", item.ToString());
Console.Mriteline(*);
}
}
class Program
{
static void Kain()
{
//创建各种类型的数糕
var intArray … new int[] {
3, 5, 7, 9, 11 };
var stringArray - new string[] {
"first", "second", "third" };
var doubleArray * nex double[]{
3.567, 7.891, 2.345 };
Simple.ReverseAndPrintcint>(intArray); // 调用方法
Simple.ReverseAndPrint(intArray); // 推断类型并调用
Simple.ReverseAndPrint<string>(stringArray); // 调用方法
Simple.ReverseAndPrint(stringArray); // 推断类型并调用
Simple.ReverseAndPrintcdouble>(doubleArray); // 调用方法
Simple.ReverseAndPrint(doubleArray); // 推断类型并调用
}
}
The result of the above case:
11, 9, 7, 5, 3
3, 5, 7, 9, 11
third, second, first
first, second, third
2.345, 7.891
, 3.567 3.567, 7.891, 2.345
Extension methods and generic classes
Extension methods of generic classes :
- Must be declared as static
- Must be a member of static class
- There must be the keyword this in the first parameter type, followed by the name of the extended generic class
Case
static class ExtendHolder
{
public static void Print<T>(this Holder<T> h)
{
T[] vals= h.GetValues();
Console.NriteLine("{0},\t{1},\t{2}",vals[0], vals[1],vals[2]);
}
}
class Holder<T>
{
T[] Vals = new T[3];
public Holder(T V0, T V1,T v2)
{
vals[0] = v0;
Vals[1] = v1;
Vals[2] = v2;
}
public T[] GetValues()
{
return Vals;
}
class Program
{
static void Main(string[] args)
{
var intHolder = new Holder<int>(3, 5, 7);
var stringHolder = new Holder<string>("a1", "b2", "c3");
intHolder.Print();
stringHolder.Print();
}
}
Result of the above case:
3, 5, 7
a1, b2, c3
Generic structure
- Similar to generic classes, the generic structure can have type parameters and constraints
- The rules and conditions of the paradigm structure are the same as the generic class
Case
struct PieceOfData<T> //泛型结构
{
public PieceOfData(T value)
{
_data = value;
}
private T _data;
public T Data
{
set _data = value;
{
}
class Program
{
static void Main()
{
// 构造类型
var intData = new Piece0fData<int>(10);
// 构造类型
var stringData = new PieceOfData<string>("Hi there.");
Console.WriteLine("intData = {0}", intData.Data);
Console.WriteLine("stringData = {0}", stringData.Data);
}
}
Case result:
intData = 10
stringData = Hi there
Generic delegate
- Generic delegates and non-generic delegates are very similar, but the type parameter determines what methods can be accepted
- To declare a generic delegate, place the type parameter list in angle brackets after the delegate name and before the delegate parameter list
Case
delegate void MyDelegate<T>(T value); // 泛型委托
class Simple
{
static public void PrintString(string s) // 方法匹配委托
{
Console.WriteLine(s);
}
static public void PrintUpperString(string s) // 方法匹配委托
{
Console.MriteLine("(O)",s.ToUpper());
}
}
class Program
{
static void Main()
{
//创建委托的实例
var myDel = new MyDelegate<string>(Simple.PrintString);
//添加方法
myDel += Simple.PrintUpperString;
//调用委托
myDel("Hi There.");
}
}
Case result:
Hi There
Hi There
Generic interface
Generic interfaces allow us to write interfaces whose parameters and interface member return types are generic type parameters. The declaration of a generic interface is similar to the declaration of a non-generic interface, but the type parameters need to be placed in angle brackets after the interface name
Case
interface IMyIfc<T> // 泛型接口
{
T RetrnIt( T inValue);
}
// 类型参数 泛型接口
class Simple<S> :IMyIfc<S> // 泛型类
{
public S ReturnIt(S inValue) //实现泛型接口
{
return inValue
}
}
class Program
{
static void Main()
{
var trivInt = new Simple<int>();
var trivString = new Simple<string>();
Console.WriteLine("{0)",trivInt.ReturnIt(5));
Console.NriteLine("{o)",trivString.ReturnIt("Hi there."));
}
}
Case result:
5
Hi there.
ps: The implementation of the generic interface must be unique