發佈於?2018-11-14
这一篇,讲讲泛型。
没有泛型的时候,我们封装的行为都是作用在特定类型上的,但是,很多时候如果我们把行为提取或重构出来,使其可以应用到很多类型上去的话,那么就会更有意义。这也是泛型出现的原因。
我们可以额外增加一层抽象,这样类型就不用再硬编码了,这样就可以使得多段代码在不同类型执行相同的指令成为可能。
之前写 Objective-C 的时候,感觉他的泛型太弱了,现在写 C#,感觉他的泛型系统太好用了!
class MyIntStack
{
int StackPoint = 0;
int[] StackArray;
public void push(int x)
{
//...
}
public int pop()
{
//...
}
}
如果希望将相同的功能应用于 float 类型,我们就必须复制粘贴上面的代码再改成 float。 这样做可行,但是缺点明显:
泛型(generic)提供了更优雅的解决方案,让多个类型共享一组代码。我们可以使用类型占位符书写代码,在创建实例时指明实际类型即可。
C# 提供了 5 种泛型可用在的地方: 类、结构、接口、委托、方法。前四种是类型,最后一种是成员。
class MyStack<T>
{
int StackPointer = 0;
T[] StackArray;
public void push(T x)
{
//...
}
public T pop()
{
//...
}
}
上面我们已经展示了一个示例来演示泛型类,这里我们具体介绍一下,看看如何创建并使用泛型类。 创建使用非泛型类有两步: 声明类和创建类的实例。 但是泛型类不是实际类,而是类的模板,所以我们必须先从模板构建出实际类型,然后创建这个构建后的类型的实例。
声明步骤:
class SomeClass <T1, T2>
{
public T1 SomeVal = new T1();
public T2 OtherVal = new T2();
}
列出类名并在尖括号中提供真实类型代替类型参数,我们将替代类型参数的真实类型称为类型实参(type argument)。
SomeClass<short, int>
非泛型类创建实例:
MyNonGenClass myNGC = new MyNonGenClass();
泛型类创建实例:
SomeClass<short, int> mySc1 = new SomeClass<short, int>();
为了让泛型变得更有用,我们需要提供额外信息让编译器知道 type parameter 可以接受哪些类型。 这些额外信息称为约束(constraint)。
where 子句使用要点:
class MyClass<T1, T2, T3>
where T2: Customer
where T3: IComparable
{
//...
}
共有 5 种类型的约束:
顺序:
class SortedList<S>
where S: IComparable<S> { ... }
class LinkedList<M, N>
where M: IComparable<M>
where N: ICloneable { ... }
class MyDictionary<KeyType, ValueType>
where KeyType: IEnumerable, new() { ... }
与其他的泛型不同,泛型方法是成员而不是类型,它可以用于泛型、非泛型类、结构或接口。
泛型方法具有类型参数和可选的约束。 泛型方法有两个参数列表:
要声明泛型方法:
public void PrintData<S, T> (S s, T t) where S: Person
{
//...
}
要调用泛型方法,需要在调用时提供类型实参:
MyMethod<short, int>();
如果我们在调用泛型方法时,可以从方法参数推断出类型实参,则可以省略类型实参:
public void MyMethod<T> (T t)
{
//...
}
int IntVal = 10;
MyMethod(IntVal);
与泛型类一样,泛型结构也有类型参数和约束子句。
struct PieceOfData <T>
{
private T _Data;
public PieceOfData(T data)
{
_data = data;
}
public T Data
{
set { _data = value; }
get { return _data; }
}
}
class Program
{
static void Main()
{
var intData = new PieceOfData<int>(10);
Console.WriteLine($"{intData.Data}");
}
}
泛型委托与非泛型委托类似,要声明泛型委托,需要在委托名称之后,委托参数列表之前放置类型参数列表:
delegate R MyDelegate <T, R>(T t);
C# 中有两个常用的预定义的泛型委托: Func 和 Action:
public delegate TR Func <T1, T2, TR>(T1 p1, T2 p2);
class Simple
{
static string PrintString(int a, int b)
{
var total = a + b;
return total.toString();
}
}
class Program
{
static void Main()
{
var myDel = new Func <int, int, string>(Simple.PrintString);
Console.WriteLine($"{myDel(2, 3)}"); // 5
}
}
泛型接口允许我们编写参数和返回值是泛型类型参数的接口。我们需要在接口名之后用尖括号放置类型参数列表。
interface IMyIfc<T>
{
T ReturnIt(T val);
}
class Simple<S>: IMyIfc<S>
{
public S ReturnIt(S s)
{
return s;
}
}
class Program
{
static void Main()
{
var simple = new Simple<int>();
Console.WriteLine($"{simple.ReturnIt(3)}"); // 3
}
}