1、泛型类型:泛型类、泛型方法、泛型委托

 //泛型类
    public class GenericClass<T>
    {
        public void GetTypes()
        {
            Console.WriteLine(typeof(T).Name);
        }
    }

    public class GenericTypeClass
    {
        //泛型方法(指定参数)
        public void GetVoidType<M>()
        {
            Console.WriteLine(typeof(M).Name);
        }

        //泛型方法(不指定参数)
        public void GetEntityType<M>(M t, string name)
        {
            Console.WriteLine(t.GetType().Name + name);
            
        }
       //泛型委托(用于泛型参数),只能在泛型类里申明
        public delegate void Delegation<T>(T t);

        //默认三种泛型委托
        //带参数,没有返回类型
        public Action<string,int> WriteAction = (string s,int a) => Console.WriteLine(s+a);
        //带参数,有返回类型
        public Func<string,bool> FuncTionFunc = (string s)=>string.IsNullOrWhiteSpace(s);
        //一个参数,只返回bool类型
        public Predicate<string> Predication = (string str) => string.IsNullOrWhiteSpace(str);

        //泛型集合
        public Dictionary<string,object> GenericDictionary = new Dictionary<string,object>();
        public HashSet<string> GenericHashSet = new HashSet<string>();
        
    }

2、泛型约束

 //泛型约束
        //值类型参数约束
        public void MethodOfStructArgument<T>(T t) where T : struct
        {

        }
        //引用类型参数约束
        public void MethodOfObjectArgument<T>(T t) where T : class
        {

        }
        //参数必须有无参构造方法
        public void MethodOfNoArgument<T>(T t) where T : new()
        {

        }
        //接口约束
        public void MethodOfInterface<T>(T t) where T : 接口名称
        {

        }
        //父类约束
        public void MethodOfFather<T>(T t) where T : 父类名称
        {

        }

3、协变与逆变

//协变逆变只用于泛型接口或泛型委托

现存例子

Cat c = new Cat();
Animal c2 = new Cat(); //用父类接收子类,没问题
List<Cat> catList = new List<Cat>();
//List<Animal> animalList = new List<Cat>(); //编译不通过,因为虽然Animal是Cat的父类,语义正确但是编译器不允许,但是List<Animal>却不是List<Cat>的父类,没有直接的父子关系
IEnumerable<Animal> animalList = new List<Cat>();//编译通过

查看IEnumerable接口,采用了out关键字,协变

public interface IEnumerable<out T> : IEnumerable
{
      new IEnumerator<T> GetEnumerator();
}

委托类型Func也用到了协变

Func<Animal> animalFunc = new Func<Cat>(() => new Cat());

查看Func定义

public delegate TResult Func<out TResult>();

自定义实现

建立父子类

 public class Animal
    {
        public void breathe()
        {
            Console.WriteLine("呼吸");
        }
    }

    public class Cat:Animal
    {
        public void CatchJerry()
        {
            Console.WriteLine("抓老鼠");
        }
    }

2.1协变


//协变 IFoo<父类> = IFoo<子类>;左边类型参数是父类,右边类型参数是子类
//要求 T(类型参数)只能是返回值,不能是类型参数

自定义泛型接口fatherInterface及子类sonClass,及说明

public  interface fatherInterface<out T>
        {
            T getT(); //只能作为返回值,不能作为参数
           // void run(T t);//只能作为返回值,不能作为参数
        }

        public class sonClass<T> : fatherInterface<T>
        {
            public T getT()
            {
               return default(T);
            }

        }
fatherInterface<Animal> animals = new sonClass<Animal>();
                Animal an = animals.getT();//得到的是右边的子类类型,后续若有子类类型调用父类方法肯定是类型安全的

2.2逆变

//逆变:IBar<子类> = IBar<父类>;左边类型参数是子类,右边类型参数是父类

//要求 T(类型参数)只能用于参数,不能是返回值

public interface fatherInterface1<in T>
        {

            //T getT(); //只能作为参数,不能作为返回值
            void run(T t);//只能作为参数,不能作为返回值
        }

        public class sonClass1<T> : fatherInterface1<T>
        {
            public void run(T t)
            {
                
            }
        }
 fatherInterface1<Cat> cats =  new sonClass1<Animal>();
                cats.run(new Cat());//编译器编译时,以左边的参数类型为基准为Cat,所以run方法的参数要传Cat类型,这个方法真实运行时,只要求参数能满足右边类型-Animal即可,Cat一定是Animal所以也没有运行风险