1. Generic
1.1 Introducing generics: deferred declaration
When a generic method is declared, the type is not written, and the type is specified when it is called.
Postponement statement: Postpone everything can be postponed.
1.2 How to declare and use generics
Generic method: add angle brackets after the method name, which contains type parameters
The type parameter is actually a type T declaration, the method can use this type T.
As follows:
public static void Show<T>(T t)
{
Console.WriteLine($"This is {typeof(CustomMethod)},paramater={t.GetType().Name},value={t}");
}
1.3 Benefits and principles of generics
The performance of generic methods is the same as that of ordinary methods. When a method is declared by a generic, the type is not written. What type of T is. Only when it is called, do you know that a method can meet different types.
1.4 Generic class, generic method, generic interface, generic delegate
1.4.1 Generic types
One class meets different types of needs
details as follows:
public class BaseModel
{
public int Id { get; set; }
}
public class GenericClass<T>
where T: BaseModel // is a generic base class constraint
{
}
1.4.2 Generic methods
One way to meet different types of needs
details as follows:
public static void Show<T>(T t)
{
Console.WriteLine($"This is {typeof(CustomMethod)},paramater={t.GetType().Name},value={t}");
}
1.4.3 Generic interface
One interface meets different types of needs
// Generic interface
public interface IGenericInterface<T>
{
public void SayHi(T t);
}
1.4.4 Generic delegation
One delegate meets different types of needs
public delegate void Do<T>(T t);
1.5 Generic constraints
Without constraints, generics will be very limited. There are 5 types of generic constraints. as follows:
1.5.1 Base class constraints
Where T:BaseModel
BaseModel can be used as a base class
Only objects of this type or objects derived from this type can be used as type parameters. (Seal constraints are not acceptable because they are meaningless.)
// Base class
public class BaseModel
{
public int Id { get; set; }
}
// Generic class
public class GenericClass<T>
where T: BaseModel // is a generic base class constraint
{
}
transfer:
GenericConstraint.Show<BeiJing>(new BeiJing());
1.5.2 Reference type constraints
// Reference type constraints
public static T Get<T>() where T:class
{
return default (T); // default is a keyword, according to the type T returns the corresponding default
}
transfer:
GenericConstraint.Get<Person>(new Person());
1.5.3 Value type constraints
// Value type constraints
Public static D GetD<D>() where D:struct
{
return default(D);
}
transfer:
GenericConstraint.GetD<int>(116);
1.5.4 No-argument constructor
// No parameter constructor constraint
Public static S GetS<S>()
where S: new () // Mealless constructor constraint
{
return new S();
}
transfer:
GenericConstraint.GetS<Chinese>();
1.5.5 Interface constraints
// Interface constraints
public static void Show2<T>(T t) where T : ISports
{
t.Basketball();
}
transfer:
GenericConstraint.Show2<USA>(new USA());
1.6 Covariance and Inversion
The so-called covariance and inversion are all related to generics (mostly used in interfaces).
1.6.1 Covariance
Decorate the return value
Let the right side use subclasses, making it easier to use generics (subclasses to parent classes)
Out modification, after covariation can only return results, not parameters
IEnumerable<Bird> birdList=new List<Sparrow>();
// out covariant, can only return results (subclass to parent)
public interface ICustomerListOut<out T>
{
T Get();
}
public class CustomerListOut<T>:ICustomerListOut<T>
{
public T Get()
{
return default(T);
}
}
ICustomerListOut<Bird> list2 = new CustomerListOut<Sparrow>();
Func<Bird> func = new Func<Sparrow>(() => null);
IEnumerable<Bird> list3 = new List<Sparrow>();
1.6.2 Inverter
Modify incoming parameters
Make the parent class available on the right, making it easier to use generics (the parent rotor class)
In modified, can only be used as a parameter after inversion
// in Inverter can only be input parameter (parent rotor type)
public interface ICustomerListIn<in T>
{
void Show(T t);
}
public class CustomerListIn<T>:ICustomerListIn<T>
{
public void Show(T t)
{
Console.WriteLine(t.GetType().Name);
}
}
// Inverter
ICustomerListIn<Sparrow> list1 = new CustomerListIn<Bird>();
Action<Sparrow> action = new Action<Bird>((i) => { });
public interface IMyList<in inT,out outT>
{
void Show(inT t);
outT Get();
outT Do(inT t);
}
public class MyList<T, T1> : IMyList<T, T1>
{
public void Show(T t)
{
Console.WriteLine(t.GetType().Name);
}
public T1 Get()
{
Console.WriteLine(typeof(T1).Name);
return default(T1);
}
public T1 Do(T t)
{
Console.WriteLine(t.GetType().Name);
Console.WriteLine(typeof(T1).Name);
return default(T1);
}
}
IMyList<Sparrow, Bird> myList1 = new MyList<Sparrow, Bird>();
IMyList<Sparrow, Bird> myList2 = new MyList<Sparrow, Sparrow>();//协变
IMyList<Sparrow, Bird> myList3 = new MyList<Bird, Bird>();//逆变
IMyList<Sparrow, Bird> myList4 = new MyList<Bird, Sparrow>();//逆变+协变
1.7 Generic cache
Generic cache, each type will generate a different copy (suitable for different types of scenarios that need to cache a piece of data)
public class GenericCache<T>
{
private static string _TypeTime = "";
static GenericCache()
{
Console.WriteLine ("This is GenericCache static constructor");
_TypeTime = $"{typeof(T).FullName}_{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}";
}
public static string GetCache()
{
return _TypeTime;
}
}
/// <summary>
/// Dictionary cache: static attributes reside in memory
/// </summary>
public class DictionaryCache
{
private static Dictionary<Type, string> _TypeTimeDictionary = null;
static DictionaryCache()
{
Console.WriteLine ("This is DictionaryCache static constructor");
_TypeTimeDictionary = new Dictionary<Type, string>();
}
public static string GetCache<T>()
{
Type type = typeof(Type);
if (!_TypeTimeDictionary.ContainsKey(type))
_TypeTimeDictionary[type] = $"{typeof(T).FullName}_{DateTime.Now.ToString("yyyy-MM-dd HH mm:ss")}";
return _TypeTimeDictionary[type];
}
}