public class CommonClass { public static void ShowInt(int iValue) { //typeof(CommonClass) typeof关键字 获取一个类型的类型 Console.WriteLine(" ShowInt方法 展示{0},其类型为{1}", iValue, typeof(int)); } public static void ShowLong(long lValue) { //lValue.GetType() 变量.GetType() 获取一个变量的类型 Console.WriteLine(" ShowLong方法 展示{0},其类型为{1}", lValue, lValue.GetType()); } public static void ShowString(string sValue) { Console.WriteLine(" ShowString方法 展示{0},其类型为{1}", sValue, sValue.GetType()); } public static void ShowDateTime(DateTime dValue) { Console.WriteLine(" ShowDateTime方法 展示{0},其类型为{1}", dValue, dValue.GetType()); } /// <summary> /// 1 通过继承,子类拥有父类的一切属性和行为,任何父类出现的地方,子类都可以代替 /// 2 object 类型是一切类型的父类 /// </summary> /// <param name="oValue"></param> public static void ShowObject(object oValue) { Console.WriteLine(" ShowObject方法 展示{0},其类型为{1}", oValue, oValue.GetType()); } }
每种方法都需要声明一个,作为一名程序员都知道这样是不对的,效率太低。
但是用object类型则需要多次的装箱和拆箱操作,效率上实在是不太好了。
所以有了泛型,泛型就是先不声明类型,用占位符替代类型的位置,在使用的时候再指定类型。
下面引入泛型:
通过一个方法完成多个不同参数类型的方法-泛型方法
1 /// <summary> /// 延迟声明:推迟一切可以推迟的东西 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tValue"></param> public static void Show<T>(T tValue)//Show`1 1是泛型类型的个数 { Console.WriteLine(" Show<T>方法 展示{0},其类型为{1}", tValue, tValue.GetType()); }
基础类:
public class BaseModel { public int Id { get; set; } public string Name { get; set; } } public class People : BaseModel { public void SayHi() { Console.WriteLine("{0} 吃了吗", base.Name); } } public class Chinese : People, ISport,IGame { public string Majiang { get; set; } public void Pingpang() { } public void Yumao() { } public void Chess() { } } public class Japanese { public int Id { get; set; } public string Name { get; set; } public void SayHi() { Console.WriteLine("{0} ^*^&^&%&", this.Name); } public string Dongman { get; set; } } public interface ISport { void Pingpang(); void Yumao(); } public class Sporter : ISport { public void Pingpang() { } public void Yumao() { } } public interface IGame { void Chess(); }
1 泛型里的<>占位符可以多个,指定占位符后可以在本方法内使用占位符所代表的类型,比如现在参数列表里 声明变量,然后再使用(占位符不一定要使用完,就像我有钱,并不一定要花完一样.) private static void Test<T, S, RUANMOU, Eleven>(T tValue, Eleven eleven)//Test`4 { Console.WriteLine(" Show<T>方法 展示{0},其类型为{1}", tValue, tValue.GetType()); }
泛型接口:
public interface IGenericInterface<T, S, W, Ruanmou> { void Query<X, Y, T, S>(); void Do(T t); }
public interface IInterface { void Foo(); }
声明泛型类的可能遇到的两种情况
1 参数列表要和继承的保持一致,继承者的参数列表可以多但不能比被继承的参数少.
public class ClassFromInterface<T, S, W, Ruanmou,liuyJ> : IInterface, IGenericInterface<T, S, W, Ruanmou> { public void Foo() { throw new NotImplementedException(); } public void Query<X, Y, T, S>() { throw new NotImplementedException(); } public void Do(T t) { throw new NotImplementedException(); } }
2.在继承的泛型接口里直接指定类型
public class ClassFromInterfaceCommon : IInterface, IGenericInterface<int, string, DateTime, IInterface> { public void Foo() { throw new NotImplementedException(); } public void Query<X, Y, T, S>() { throw new NotImplementedException(); } public void Do(int t) { throw new NotImplementedException(); } }
泛型约束:享受约束,拥有权力.
/// <summary> /// 基类约束是第一个,而且只能有一个People /// 接口约束可以多个 ISport,IGame /// 值类型和引用类型约束本身不能共存,,而且有了基类约束后,也不需要二者 /// new只能放在最后面 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tValue"></param> public static void Show<T>(T tValue) where T : People, ISport, IGame, new() { System.Console.WriteLine(tValue.Name); System.Console.WriteLine(tValue.Id); tValue.SayHi(); tValue.Pingpang(); tValue.Yumao(); tValue.Chess(); T t = new T(); Console.WriteLine(" Show<T>方法 展示{0},其类型为{1}", tValue, tValue.GetType()); }
对比以下两个方法,一个是泛型方法,另一个是去掉泛型<>,直接指定People.
public static void ShowPeople<T>(T tValue) where T : People { System.Console.WriteLine(tValue.Name); System.Console.WriteLine(tValue.Id); tValue.SayHi(); Console.WriteLine(" Show<T>方法 展示{0},其类型为{1}", tValue, tValue.GetType()); } public static void ShowPeople(People tValue) { System.Console.WriteLine(tValue.Name); System.Console.WriteLine(tValue.Id); tValue.SayHi(); Console.WriteLine(" Show<T>方法 展示{0},其类型为{1}", tValue, tValue.GetType()); }
从这里就看出了约束的作用了,使用它的类型必须是People类型或者它的子类.
任何父类出现的地方都可以用子类来替代.
class和struct约束
private T Get<T>() //where T : class//表示T是引用类型 //where T : struct//表示T是值类型 where T : new()//表示T有一个无参数的构造函数 { T t = new T(); return default(T); }
接口约束:
public static void ShowPeopleInterface<T>(T tValue) where T : ISport { tValue.Yumao(); tValue.Pingpang(); Console.WriteLine(" Show<T>方法 展示{0},其类型为{1}", tValue, tValue.GetType()); }
协变out--修饰返回值--子到父. | 逆变in--修饰传入参数值--父到子.
主要用在泛型接口和泛型委托上
基本类,后面会用到:
public class Parent { public int Id { get; set; } } public class Child : Parent { public string Name { get; set; } }
in和out的接口
public interface ICustomerListIn<in T> { void Show(T t);//使用传入参数 } public interface ICustomerList<out T> { T Get();//返回 }
接口继承:
out
public interface ICustomerList<out T> { T Get(); } public class CustomerList<T> : ICustomerList<T> { public T Get() { return default(T); } }
in
public interface ICustomerListIn<in T> { void Show(T t); } public class CustomerListIn<T> : ICustomerListIn<T> { public void Show(T t) { } }
----------------------------------------------------------那么正题来了-----------------------------------------------------------------------------------------------------------
1. 一般情况下,子类可以初始化基类,但是,基类是不可以初始化子类的.
{ Parent parent1 = new Parent(); Parent parent2 = new Child(); Child child1 = new Child(); //Child child2 = new Parent();---报错了哦! }
儿子以后可以当老子,老子却不能当儿子(这里只说父子,没有爷爷.)
2. 初学者的误区:在List<>里面指定父类和子类
比如:Parent和Child有继承和派生的关系,但是,并不代表着List<Parent>和List<Child>有半毛钱的关系.
因为List<Parent>和List<Child>分别是新的类型而且是不同的List<泛型集合>类型.
{ List<Parent> parentList1 = new List<Parent>(); //List<Parent> parentList2 = new List<Child>();//ParentList类型和ChildList类型没有父子关系,所以即使Perent和Child有父子关系也不能这么玩 List<Parent> parentList3 = new List<Child>().Select(c => (Parent)c).ToList(); }
3.添加有out的就可以咯
namespace System.Collections.Generic { // 摘要: // 公开枚举数,该枚举数支持在指定类型的集合上进行简单迭代。 // // 类型参数: // T: // 要枚举的对象的类型。 [TypeDependency("System.SZArrayHelper")] public interface IEnumerable<out T> : IEnumerable { // 摘要: // 返回一个循环访问集合的枚举器。 // // 返回结果: // 可用于循环访问集合的 System.Collections.Generic.IEnumerator<T>。 IEnumerator<T> GetEnumerator(); } }
系统的协变out
{ IEnumerable<Parent> parentList1 = new List<Parent>(); IEnumerable<Parent> parentList2 = new List<Child>();//ParentList类型和ChildList类型没有父子关系 }
4. out-子到父-协变
{ ICustomerList<Parent> customerList1 = new CustomerList<Parent>(); ICustomerList<Parent> customerList2 = new CustomerList<Child>(); }
5.in-父到子-逆变
1 { ICustomerListIn<Child> customerList2 = new CustomerListIn<Child>(); customerList2.Show(new Child()); ICustomerListIn<Child> customerList1 = new CustomerListIn<Parent>(); customerList1.Show(new Child()); ICustomerListIn<Parent> parentList1 = new CustomerListIn<Parent>(); parentList1.Show(new Child()); parentList1.Show(new Parent()); }
6.out和in都有的情况,都可以咯.
public class MyList<T1, T2> : IMyList<T1, T2> { public void Show(T1 t) { Console.WriteLine(t.GetType().Name); } public T2 Get() { Console.WriteLine(typeof(T2).Name); return default(T2); } public T2 Do(T1 t) { Console.WriteLine(t.GetType().Name); Console.WriteLine(typeof(T2).Name); return default(T2); } }
public interface IMyList<in inT, out outT> { //out 只能是返回值 in只能是参数 void Show(inT t); outT Get(); outT Do(inT t); }
总体来说: out和in的功能就是告诉编译器不报错.
out,是协变嘛,协助变换,顺向的,就是子类到父类.所以可以返回.
但不能当参数用,因为子类变父类是有限制的.
in,是逆变呗,逆向变换,逆向的,就是父类到子类.只能用在参数里.
讲道理,它是违规的,所以呢,不能返回,当参数运算还是可以得.