一、为什么要使用泛型?看下面代码:
class Test
{
public void OutputValue(int value)
{
Console.WriteLine(value .ToString ());
}
public void OutputValue(double value)
{
Console.WriteLine(value.ToString());
}
public void OutputValue(String value)
{
Console.WriteLine(value.ToString());
}
}
我们写三个方法就为了输出不同类型的值,这也太复杂了吧?明明我们可以使用object类型,代替一切类型,代码真简洁,但是object是引用类型,如果输入的变量是值类型则不可避免的要进行装箱从而降低了系统的性能,如果是大量的数据,这个性能的损失就比较大了,如下:
class Test
{
public void OutputValue(object value)
{
Console.WriteLine(value .ToString ());
}
}
我们有没有别的方法呢?当然有喽,泛型就是解决这个问题的,如下:代码还是一样的简洁,也不需要进行装箱降低系统性能,真香!
class Test
{
public void OutputValue(T value)
{
Console.WriteLine(value .ToString ());
}
}
Test test = new Test();//我们在使用的时候指定类型就好。
int value1 = 100;
test.OutputValue(value1);//Ok
double value2 = 29.5;
test.OutputValue(value2 );//报错,因为泛test引用的对象实例化过程中已经指定了参数类型。
二、类型参数的命名规范
class Country<TCountry>
{
public void DisplayCountryName(TCountry country)
{
if (country is American)
{
American american = country as American;
Console.WriteLine(american.Name);
}
if (country is China)
{
China china = country as China;
Console.WriteLine(china.Name);
}
}
}
class American
{
public string Name { get; set; } = "美国";
}
class China
{
public string Name { get; set; } = "中国";
}
调用:
Country<American> country = new Country<American>();
country.DisplayCountryName(new American ());
以上代码中输出的结果是国家的名称,那么将泛型类的类型参数命名的跟国家相关就比较有意义,而不是让人看了感觉这个参数与我们想要传输的参数毫不相关,所以这里的参数命名为TCountry。
三、泛型接口和结构
声明泛型接口
interface IPair<T>
{
T First { get; set; }
T Second { get; set; }
}
实现泛型接口
struct Pair<T>:IPair <T>
{
public Pair(T first)//如果构造函数中只传入一个参数,但是由于结构中的字段必须在构造中全部初始化,所以对自动实现的属性Second赋予了一个默认值default(T),这里还涉及到一个实现泛型类型的构造器的语法格式
this.First = first;
this.Second = default(T);
}
public T First { get; set; }
public T Second { get; set; }
}
在类中多次实现同一个接口
interface ITest<T>//接口定义
{
T Age { get; set; }
double GetHeight(T t);
}
class German : ITest<int>, ITest<double>//实现接口
{
int ITest<int>.Age { get; set; }
double ITest<int>.GetHeight(int height)
{
bool isConvert = false;
double value;
isConvert = double.TryParse(height.ToString(), out value);
if (isConvert)
return value;
else
return 0;
}
double ITest<double>.Age { get; set; }
double ITest<double>.GetHeight(double height)
{
bool isConvert = false;
double value;
isConvert = double.TryParse(height.ToString(), out value);
if (isConvert)
return value;
else
return 0;
}
}
但是最好避免在类型中实现同一个泛型接口的多个构造
四、多个类型参数
泛型类型可以使用任意数量的类型参数,如下:
interface ICoordinate<TX,TY>//接口定义
{
TX X { get; set; }
TY Y { get; set; }
}
class Coordinate<TX, TY> : ICoordinate<TX, TY>//接口实现
{
public Coordinate(TX tX, TY tY)
{
this.X = tX;
this.Y = tY;
}
public TX X { get; set; }
public TY Y { get; set; }
}
五、元组
元组是一个泛型类,并且支持多个类型参数个数的重载,如下:
Tuple
string value = tuple.Item1;
double value2 = tuple.Item2;
Tuple tuple2 = Tuple.Create("B",12.0,10,'c');//如果参数特别多的时候,如果不使用工厂方法创建对象的话,代码的长度就比较长,所以可以利用Tuple元组类型推断的特点并结合工厂方法可以简化代码。
六、嵌套泛型类
```csharp
class Container<T, U>
{
public Container( T t,U u)
{
this.Age = t;
}
public T Age { get; set; }
public void Test()
{
Nest<double> nest = new Nest<double>();
nest.Method(Age,100);
}
private class Nest<U>
{
public void Method(T param0, U param1)
{
Console.WriteLine($"T:{param0}\nU:{param1 }");
}
}
}
Container为包容类,Nest为嵌套类,嵌套类有两个特性:
1)加入包容类型声明类型参数为T,那么T可以在嵌套类中使用,但是如果在嵌套类中声明了和包容类一样的类型参数,则它会隐藏包容类的类型参数,
2)避免在嵌套类中使用和包容类参数一样的类型参数。
七、约束
1)接口约束
interface ITest
{
int Age { get; set; }
double GetHeight(int t);
}
class Test2 : ITest
{
public int Age { get; set; }
public double GetHeight(int t)
{
return 10;
}
}
class Test3<T> where T : ITest//约束指示类型参数T必须是Test或者Test的子类
{
public void Test()
{
Console.WriteLine("我在测试");
}
}
Test3<Test2> test = new Test3<Test2>();//这里T使用Test类
test.Test();
Test3<int> test = new Test3<int>();//报错,因为int不是Test或者Test的子类
test.Test();
2)类约束
class Test
{
public Test(int age)
{
this.Age = age;
}
public int Age { get; set; }
public double GetHeight(int t)
{
return 100;
}
}
class Test3<T> where T : Test //指定Test类约束
{
public void Test()
{
Console.WriteLine("我在测试");
}
}
调用:
Test3<Test > ddd = new Test3<Test>();
ddd.Test();
3)struct/classs约束
struct Test
{
public Test(int age)
{
this.Age = age;
}
public int Age { get; set; }
public double GetHeight(int t)
{
return 100;
}
}
class Test3<T> where T : struct //将类型参数限制为任何非可空的值类型
{
public void Test()
{
Console.WriteLine("我在测试");
}
}
调用
Test3<Test > ddd = new Test3<Test>();
ddd.Test();
如果使用下面的int?就会出错,因为struct规定了传入的类型参数必须是非null的类型
Test3
ddd.Test();
class Test
{
public Test(int age)
{
this.Age = age;
}
public int Age { get; set; }
public double GetHeight(int t)
{
return 100;
}
}
class Test3<T> where T : class //将类型参数限定为引用类型
{
public void Test()
{
Console.WriteLine("我在测试");
}
}
调用:
Test3<Test > ddd = new Test3<Test>();//由于Test为引用类型所以Ok
ddd.Test();
如果是Test3 ddd = new Test3();这就会报错,因为int不是引用类型
4)多个约束
class Test:IComparable ,IFormattable
{
public Test(int age)
{
this.Age = age;
}
public int Age { get; set; }
public double GetHeight(int t)
{
return 100;
}
public int CompareTo(object obj)
{
Test test = obj as Test;
return test == this ? 0 : 1;
}
public string ToString(string format, IFormatProvider formatProvider)
{
return null;
}
}
class Test3<T> where T : Test,IComparable, IFormattable//只能有一个类约束,因为类只能有一个父类
//但是可以有很多个接口,因为类可以实现多个接口
{
public void Test()
{
Console.WriteLine("我在测试");
}
}
调用:
Test3<Test > ddd = new Test3<Test>();
ddd.Test();
5)构造器约束
class Test:IComparable ,IFormattable
{
public Test(int age)
{
this.Age = age;
}
public Test()//无参构造函数
{
}
public int Age { get; set; }
public double GetHeight(int t)
{
return 100;
}
public int CompareTo(object obj)
{
Test test = obj as Test;
return test == this ? 0 : 1;
}
public string ToString(string format, IFormatProvider formatProvider)
{
return null;
}
}
class Test3<T> where T : Test,IComparable, IFormattable ,new () //构造器约束的语法
{
public void Test()
{
Console.WriteLine("我在测试");
T t = new T();//因为要实例化类型参数,所以需要约束构造器,且为无参的构造器
}
}
调用:
Test3<Test > ddd = new Test3<Test>();
ddd.Test();
6)约束继承
class Test3 where T : Test
{
public void Test()
{
Console.WriteLine(“我在测试”);
}
}
class Test5:Test3 //会报错,因为Test5没有指定约束的类型
{
}
正确的如下:
class Test5:Test3 where T:Test//使用和父类Test3 一样的约束,相当于继承了父类的约束
{
}
8、泛型方法
class Test
{
public string GetHeight<T>(T t)where T:Test6//泛型方法以及约束
{
return t.ToString ();
}
}
class Test6
{
}
Test ddd = new Test();
Test6 test6 = new Test6();
ddd.GetHeight <Test6>(test6);
9、协变与逆变
参考博文协变与逆变
1)协变
interface IPair< T>
{
}
class Pair<T>:IPair < T>
{
}
class Father
{
}
class Son:Father
{
}
调用:
IPair father = new Pair();
IPair son = new Pair();
father = son;//报错
即使son类是father的子类,但是也会报错,改变代码如下:
interface IPair
{
}
报错就消失,因为out参数指出了泛型接口对T这个类型参数协变。
2)逆变
interface IPair< T>
{
}
IPair<Father> father = new Pair<Father>();
IPair<Son> son = new Pair<Son>();
son = father;//报错
改变代码如下:
interface IPair<in T>
{
}
报错就消失,因为in参数指出了泛型接口对T这个类型参数逆变。