按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
使用结构约束,类型T必须是值类型
where T : class
类约束指定,类型T必须是引用类型
where T : IFoo
指定类型T必须执行接口IFoo
where T : Foo
指定类型T必须派生于基类Foo
where T : new()
这是一个构造函数约束,指定类型T必须有一个默认构造函数
where T : U
这个约束也可以指定,类型T1派生于泛型类型T2。该约束也称为裸类型约束
注意:
在CLR 2。0中,只能为默认构造函数定义约束,不能为其他构造函数定义约束。
使用泛型类型还可以合并多个约束。where T : IFoo,new()约束和MyClass声明指定,类型T必须执行IFoo接口,且必须有一个默认构造函数。
public class MyClass
where T : IFoo; new()
{
//。。。
提示:
在C#中,where子句的一个重要限制是,不能定义必须由泛型类型执行的运算符。运算符不能在接口中定义。在where子句中,只能定义基类、接口和默认构造函数。
9。3。3 继承
前面创建的LinkedList类执行了IEnumerable接口:
public class LinkedList : IEnumerable
{
//。。。
泛型类型可以执行泛型接口,也可以派生于一个类。泛型类可以派生于泛型基类:
public class Base
{
}
public class Derived : Base
{
}
其要求是必须重复接口的泛型类型,或者必须指定基类的类型,如下所示:
public class Base
{
}
public class Derived : Base
{
}
于是,派生类可以是泛型类或非泛型类。例如,可以定义一个抽象的泛型基类,它在派生类中用一个具体的类型实现。这允许对特定类型执行特殊的操作:
public abstract class Calc
{
public abstract T Add(T x; T y);
public abstract T Sub(T x; T y);
}
public class SimpleCalc : Calc
{
public override int Add(int x; int y)
{
return x + y;
}
public override int Sub(int x; int y)
{
return x … y;
}
}
9。3。4 静态成员
泛型类的静态成员需要特别关注。泛型类的静态成员只能在类的一个实例中共享。下面看一个例子。StaticDemo类包含静态字段x:
public class StaticDemo
{
public static int x;
}
由于对一个string类型和一个int类型使用了StaticDemo类,所以存在两组静态字段:
StaticDemo。x = 4;
StaticDemo。x = 5;
Console。WriteLine(StaticDemo。x); // writes 4
9。4 泛型接口
使用泛型可以定义接口,接口中的方法可以带泛型参数。在链表示例中,就执行了IEnumerable接口,它定义了GetEnumerator()方法,以返回IEnumerator。对于 1。0中的许多非泛型接口, 从2。0开始定义了新的泛型版本,例如Iparable:
public interface Iparable
{
int pareTo(T other);
}
第5章中的非泛型接口Iparable需要一个对象,Person类的pareTo()方法才能按姓氏给人员排序:
public class Person : Iparable
{
public int pareTo(object obj)
{
Person other = obj as Person;
return this。lastnamepareTo(other。lastname);
}
//。。。
执行泛型版本时,不再需要将object的类型强制转换为Person:
public class Person : Iparable
{
public int pareTo(Person other)
{
return this。lastnamepareTo(other。lastname);
}
//。。。
9。5 泛型方法
除了定义泛型类之外,还可以定义泛型方法。在泛型方法中,泛型类型用方法声明来定义。
Swap方法把T定义为泛型类型,用于两个参数和一个变量temp:
void Swap(ref T x; ref T y)
{
T temp;
temp = x;
x = y;
y = temp;
}
把泛型类型赋予方法调用,就可以调用泛型方法:
int i = 4;
int j = 5;
Swap(ref i; ref j);
但是,因为C#编译器会通过调用Swap方法来获取参数的类型,所以不需要把泛型类型赋予方法调用。泛型方法可以像非泛型方法那样调用:
int i = 4;
int j = 5;
Swap(ref i; ref j);
下面的例子使用泛型方法累加集合中的所有元素。为了说明泛型方法的功能,下面的Account类包含name和balance:
public class Account
{
private string name;
public string Name
{
get
{
return name;
}
}
private decimal balance;
public decimal Balance
{
get
{
return balance;
}
}
public Account(string name; Decimal balance)
{
this。name = name;
this。balance = balance;
}
}
应累加结余的所有账目操作都添加到List类型的账目列表中:
List accounts = new List();
accounts。Add(new Account(〃Christian〃; 1500));
accounts。Add(new Account(〃Sharon〃; 2200));
accounts。Add(new Account(〃Katie〃; 1800));
累加所有Account对象的传统方式是用foreach语句迭代所有的Account对象,如下所示。foreach语句使用IEnumerable接口迭代集合的元素,所以AccumulateSimple()方法的参数是IEnumerable类型。这样,AccumulateSimple()方法就可以用于所有实现IEnumerable接口的集合类。在这个方法的实现代码中,直接访问Account对象的Balance属性:
public static class Algorithm
{
public static decimal AccumulateSimple(IEnumerable e)
{
decimal sum = 0;
foreach (Account a in e)
{
sum += a。Balance;
}
return sum;
}
}
Accumulate()方法的调用方式如下:
decimal amount = Algorithm。AccumulateSimple(accounts);
第一个实现代码的问题是,它只能用于Account对象。使用泛型方法就可以避免这个问题。
Accumulate()方法的第二个版本接受实现了IAccount接口的任意类型。如前面的泛型类所述,泛型类型可以用where子句来限制。这个子句也可以用于泛型方法。Accumulate()方法的参数改为IEnumerable。IEnumerable是IEnumerable接口的泛型版本,由泛型集合类实现。
public static decimal Accumulate(IEnumerable coll)
where TAccount : IAccount
{
decimal sum = 0;
foreach (TAccount a in coll)
{
sum += a。Balance;
}
return sum;
}
Account类现在重构为执行接口IAccount:
public class Account : IAccount
{
//。。。
IAccount接口定义了只读属性Balance和Name:
public interface IAccount
{
decimal Balance { get; }
string Name { get; }
}
将Account类型定义为泛型类型参数,就可以调用新的Accumulate()方法:
decimal amount = Algorithm。Accumulate(accounts);
因为编译器会从方法的参数类型中自动推断出泛型类型参数,所以以如下方式调用Accumulate()方法是有效的:
decimal amount = Algorithm。Accumulate(accounts);
泛型类型实现IAccount接口的要求过于严厉。这个要求可以使用泛型委托来改变。在下一节中,Accumulate()方法将改为独立于任何接口。
9。6 泛型委托
如第7章所述,委托是类型安全的方法引用。通过泛型委托,委托的参数可以在以后定义。
Framework定义了一个泛型委托EventHandler,它的第二个参数是TEventArgs类型,所以不再需要为每个新参数类型定义新委托了。
public sealed delegate void EventHandler(object sender; TEventArgs e)
where TEventArgs : EventArgs
9。6。1 执行委托调用的方法
把Accumulate()方法改为有两个泛型类型。TInput是要累加的对象类型,TSummary是返回类型。Accumulate的第一个参数是IEnumerable接口,这与以前相同。第二个参数需要Action委托引用一个方法,来累加所有的结余。
在实现代码中,现在给每个元素调用Action委托引用的方法,再返回计算的总和:
public delegate TSummary Action(TInput t; TSummary u);
public static TSummary Accumulate(IEnumerable coll;
Action action)
{
TSummary sum = default(TSummary);
foreach (TInput input in coll)
{
sum = action(input; sum);
}
return sum;
}