Learning about generics
generic learning
1. Introduction of generics
- Generics—General: broad–uncertain; type: type—uncertain type
- Everywhere
- When calling a normal method, the parameter type is determined when it is declared. Just pass the parameters according to the type
a. If there are 100 types—100 methods? –very tired
b. Is there a method that can meet different types of needs?
Traditional methods
public static class CommonMethod
{
public static void ShowInt(int Parameter)
{
Console.WriteLine($"This is {typeof(CommonMethod).Name} parameter={Parameter},type={Parameter.GetType().Name} ");
}
public static void ShowString(string Parameter)
{
Console.WriteLine($"This is {typeof(CommonMethod).Name} parameter={Parameter},type={Parameter.GetType().Name} ");
}
public static void ShowDateTime(DateTime Parameter)
{
Console.WriteLine($"This is {typeof(CommonMethod).Name} parameter={Parameter},type={Parameter.GetType().Name} ");
}
}
- Object type as parameter—-different parameters can be passed
a. Any address appearing in a subclass can be replaced by the parent class
b. Everything is an object—any type inherits from Object
Use Object type
public static class CommonMethod
{
public static void ShowObject(object Parameter)
{
Console.WriteLine($"This is {typeof(CommonMethod).Name} parameter={Parameter},type={Parameter.GetType().Name} ");
}
}
- Question
a. Performance issues —Boxing and unboxing—In C# syntax, the type is determined according to the declaration (stack, managed heap)
b. Type safety issues
We use an example to illustrate the performance issue
public class PerformanceTest
{
public static void Show()
{
int ivalue = 1234;
//time consumed
long commonSecond = 0;
long objectSecond = 0;
long genericSecond = 0;
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 1000_000_000; i++) {
ShowInt(ivalue);
}
sw.Stop();
commonSecond = sw.ElapsedMilliseconds;
}
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 1000_000_000; i++)
{
ShowObject(ivalue);
}
sw.Stop();
objectSecond = sw.ElapsedMilliseconds;
}
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 1000_000_000; i++)
{
Show(ivalue);
}
sw.Stop();
genericSecond = sw.ElapsedMilliseconds;
}
Console.WriteLine($"commonSecond: {commonSecond} objectSecond: {objectSecond} genericSecond :{genericSecond} ");
}
private static void ShowInt(int r)
{
}
private static void ShowObject(object o)
{
}
private static void Show(T parameter)
{
}
}
The result is:
- Is there a method that has good performance and can support multiple types? —Generic methods
a. The declaration has an extra pair of angle brackets + placeholder T
b. Calling — also requires an additional pair of angle brackets. The type specified in the angle brackets must be consistent with the type of the passed parameter.
c. If the parameter can be pushed out of the type—the angle brackets can be omitted. - Generic methods—achieve high performance—one method can meet the needs of different types:—let the horse run and let the horse eat grass
Second, generic declaration
1. Generic method: add a pair of angle brackets after the method name, with placeholders in the angle brackets
2. Delayed declaration: When declaring, just give a placeholder T. What type is T? I don’t know what type—when you call, you specify what you are, and when you call, it is what you say;
3. Placeholder: T —type parameter — type variable
4. When type parameters are used as parameters of a method, specify the parameter type.
3. Characteristics and principles of generics–How are they supported by the bottom layer?
- In high-level languages, the defined generic T must be a specific type when executed by the computer.
- How to support it at the bottom level? —As you can see at the bottom level, the generated result is LIST<c// b. You can only pass the ISports interface or the class that implements this interface.
// c. You can add functions or obtain new functions.
public static void ShowInterface(T tValue) where T : ISports
{
tValue.run();
}//Reference type constraints
// a. You can only pass the type in
public static void ShowClass(T tValue) where T : class
{}
//Value type constraints
// a. Only value types can be passed in
public static void ShowValue(T tValue) where T : struct
{}
//No parameter constructor constraints
//
public static void ShowNew(T tValue) where T : new()
{
T t= new T();
}//enumeration constraints
public static void ShowEnum(T tValue) where T : Enum
{}
Seven. Generic cache—generic class
Generic cache can generate a new copy of a class based on different types; generate countless copies;
public class GenericCacheTest { public static void Show() { //Normal cache { for (int i = 0; i < 5; i++) { Console.WriteLine(DictionaryCache.GetCache()); //GenericCacheInt Thread.Sleep(10); Console.WriteLine(DictionaryCache.GetCache());// GenericCachelong Thread.Sleep(10); Console.WriteLine(DictionaryCache.GetCache()); Thread.Sleep(10); Console.WriteLine(DictionaryCache.GetCache()); Thread.Sleep(10); Console.WriteLine(DictionaryCache.GetCache()); Thread.Sleep(10); } } //generic cache { for (int i = 0; i < 5; i++) { Console.WriteLine(GenericCache.GetCache()); //GenericCacheInt Thread.Sleep(10); Console.WriteLine(GenericCache.GetCache());// GenericCachelong Thread.Sleep(10); Console.WriteLine(GenericCache.GetCache()); Thread.Sleep(10); Console.WriteLine(GenericCache.GetCache()); Thread.Sleep(10); Console.WriteLine(GenericCache.GetCache()); Thread.Sleep(10); } } } } ///
/// Dictionary cache: static attributes are resident in memory /// public class DictionaryCache { private static Dictionary _TypeTimeDictionary = null; //The static constructor is executed only once in the entire process; static DictionaryCache() { Console.WriteLine("This is DictionaryCache static constructor"); _TypeTimeDictionary = new Dictionary(); } public static string GetCache() { Type type = typeof(T); if (!_TypeTimeDictionary.ContainsKey(type)) { _TypeTimeDictionary[type] = $"{typeof(T).FullName}_{DateTime.Now.ToString("yyyyMMddHHmmss.fff")}"; } return _TypeTimeDictionary[type]; } } //////Generic cache: /// /// public class GenericCache { static GenericCache() { Console.WriteLine("This is GenericCache static constructor"); //_TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff")); _TypeTime = $"{typeof(T).FullName}_{DateTime.Now.ToString("yyyyMMddHHmmss.fff")}"; } private static string _TypeTime = ""; public static string GetCache() { return _TypeTime; } }Seven, covariance and contravariance of generics
For example, we have an animal class and its subclass cat class
public class Animal { public int Id { get; set; } } public class Cat : Animal { public string Name { get; set; } }
We can use code like this
//Any subclass can be declared using the parent class Animal animal1 = new Cat(); //It is not necessarily possible to use subclasses to declare parent classes Cat cat2 = new Animal();
We will encounter this problem again
A cat is a bunch of animals
A bunch of cats is not a bunch of animals—speaking from a colloquial point of view, it is a bit inconsistent with human logic.List animals = new List(); //Report error List animals2 = new List();
Why? —The second category has no parent-child relationship; of course it cannot be replaced; this is determined by C# syntax.
Generics are unfriendly and uncoordinated;This introduces our covariance and contravariance;
Covariance and contravariance only apply to generic interfaces and generic delegates//Covariance allows the right side to use subclasses and the left side to use parent classes //out: Modify type parameters: you can use subclasses on the right side and parent classes on the left side. IEnumerable animals1=new List(); IEnumerable animals3= new List();
Covariance: Out type parameters can only be used as return values, not parameters
Contravariant In can only be used as a parameter and cannot be used as a return value
public interface ICustomerListIn { void show(T t); } public class CustomerListIn:ICustomerListIn { public void show(T t) { } }
Inverse code example
//Inverse variation: You can use the parent class on the right side and the subclass on the left side; ICustomerListIn customerListIn = new CustomerListIn(); ICustomerListIn customerListIn1 = new CustomerListIn();
Why are there covariance and contravariance?
What would happen if there were no covariance and contravariance?public interface ICustomerList { T Get(); void show(T t); } public class CustomerList:ICustomerList { public T Get() { return default(T); } public void show(T t) { } } } ICustomerList customerList=new CustomerList(); ICustomerList customerList1 = new CustomerList();
Covariance and contravariance of generics are like advanced constraints, designed to avoid
- Use the subclass as a parameter, but pass the parent class as a parameter;
- The subclass is used as the return value, but when returning, a parent class is returned.
lt;Animal> animals2 = new List();
Why? —The second category has no parent-child relationship; of course it cannot be replaced; this is determined by C# syntax.
Generics are unfriendly and uncoordinated;This introduces our covariance and contravariance;
Covariance and contravariance only apply to generic interfaces and generic delegates//Covariance allows the right side to use subclasses and the left side to use parent classes //out: Modify type parameters: you can use subclasses on the right side and parent classes on the left side. IEnumerable animals1=new List(); IEnumerable animals3= new List();
Covariance: Out type parameters can only be used as return values, not parameters
Contravariant In can only be used as a parameter and cannot be used as a return value
public interface ICustomerListIn { void show(T t); } public class CustomerListIn:ICustomerListIn { public void show(T t) { } }
Inverse code example
//Inverse variation: You can use the parent class on the right side and the subclass on the left side; ICustomerListIn customerListIn = new CustomerListIn(); ICustomerListIn customerListIn1 = new CustomerListIn();
Why are there covariance and contravariance?
What would happen if there were no covariance and contravariance?public interface ICustomerList { T Get(); void show(T t); } public class CustomerList:ICustomerList { public T Get() { return default(T); } public void show(T t) { } } } ICustomerList customerList=new CustomerList(); ICustomerList customerList1 = new CustomerList();
Covariance and contravariance of generics are like advanced constraints, designed to avoid
- Use the subclass as a parameter, but pass the parent class as a parameter;
- The subclass is used as the return value, but when returning, a parent class is returned.