日期:2013-01-27  浏览次数:20465 次

19.1.5泛型方法
在某些情形下,类型参数对于整个类不是必需的,而只对特定方法内是必需的。经常,当创建一个接受泛型类型作为参数的方法时就是这样。例如,当使用早先描述的Stack<T>类时,一个通用的模式可能是在一行中压入多个值,在一个单一的调用中写一个方法这么做也是很方便的。对于特定的构造类型,例如Stack<int>,这个方法看起来像这样。

void PushMultiple(Stack<int> stack ,params int[] values)

{

foreach(int value in values)

stack.Push(value);

}



这个方法可以用于压入多个int值到一个Stack<int>中。

Stack<int> stack = new Stack<int>();

PushMultiple(stack, 1,2, 3, 4);

然而,先前的方法只对于特定的构造类型Stack<int>有效。要让它对于Stack<T>也起作用,方法必须被作为泛型方法而编写。泛型方法在方法的名字后面在“<”和“>”分界符之间指定了一个或多个类型参数。类型参数可以在参数列表,返回类型和方法体之内被使用。一个泛型的PushMultiple方法将会是这样。

void PushMultiple<T>(Stack<>T stack , params T[] values)

{

foreach(T value in values) stack.Push(value);

}

使用这个泛型方法,你可以压入多个项到任意Stack<T>中。当调用一个泛型方法时,类型参数值在方法调用的尖括号中被给定。例如

Stack<int> stack = new Stack<int>();

PushMultiple<int>(stack , 1,2,3,4);

这个泛型PushMultiple方法比先前的版本更具有重用性,因为它可以工作在任何Stack<T>上,但似乎在调用的时候不太方便,因为必须提供T作为一个类型参数传递给方法。在许多情形下,编译器使用一种称为类型推断(type inferencing)处理,从传递给方法的其他参数推断正确的类型参数。在先前的例子中,因为第一个正式参数是Stack<int>类型,而后续的参数是int 类型,因此编译器可以推断类型参数值必须是int。由此,在调用泛型PushMultiple方法时可以不指定类型参数。

Stack<int> stack = new Stack<int>();

PushMultiple(stack , 1,2, 3, 4);

19.2匿名方法
事件句柄和其他回调函数经常需要通过专门的委托调用,从来都不是直接调用。虽然如此,我们还只能将事件句柄和回调函数的代码,放在特定方法中,再显式为这个方法创建委托。相反,匿名方法(anonymous method)允许一个委托关联的代码被内联的写入使用委托的地方法,很方便的是这使得代码对于委托的实例很直接。除了这种便利之外,匿名方法还共享了对本地语句包含的函数成员的访问。为了使命名方法达成共享(区别于匿名方法),需要手工创建辅助类,并将本地成员“提升(lifting)”为类的域。





下面的例子展示了一个简单的输入表单,它包含一个列表框、一个文本框和一个按钮。当按钮被按下时,在文本框中一个包含文本的项就被添加到列表框。

class InputForm:Form

{

ListBox listBox;

TextBox textbox;

Button addButton;

pubic MyForm()

{

listBox = new ListBox(…);

textbox = new TextBox(…);

addButon = new Button(…);

addButton.Click += new EventHandler(AddClick);

}

void AddClick(object sender , EventArgs e)

{

listBox.Items.Add(textbox.Text);

}

}

即使作为对按钮的Click事件的响应只有唯一的一条语句需要执行。那条语句也必须放在一个具有完整的参数列表的单独的方法中,并且还必须手工创建引用那个方法的EventHandler委托。使用匿名方法,事件处理代码将变得相当简洁。

class InputForm:Form

{

ListBox listBox;

TextBox textbox;

Button addButton;

pubic MyForm()

{

listBox = new ListBox(…);

textbox = new TextBox(…);

addButon = new Button(…);

addButton.Click +=delegate{

listBox.Items.Add(textBox.Text.);

}

}

匿名方法由关键词delegate和一个可选的参数列表,以及一个封闭在“{”和“}”分界符中的语句组成。先前的例子中匿名方法没有使用由委托所提供的参数,所以便省略了参数列表。如果要访问参数,匿名方法可以包含一个参数列表。



addButton.Click += delegate(object sender , EventArgs e){

MessageBox.Show(((Button)sender).Text);

};

在前面的例子中,将会发生一次从匿名方法到EventHandler委托类型(Click事件的类型)的隐式转换。这种隐式转换是可能的,因为参数列表和委托类型的返回值与匿名方法是兼容的。关于兼容性的确切规则如下:

如果下列之一成立,那么委托的参数列表与匿名方法是兼容的。
- 匿名方法没有参数列表,并且委托没有out 参数。

- 匿名方法包含的参数列表与委托的参数在数量、类型和修饰符上是精确匹配的。

如果下列之一成立,那么委托的返回类型与匿名方法兼容。
- 委托的返回类型是void,匿名方法没有返回语句,或者只有不带表达式的return 语句。

- 委托的返回类型不是void ,并且在匿名方法中,所有return 语句相关的表达式可以被隐式转换到委托的类型。



在委托类型的隐式转换发生以前,委托的参数列表和返回类型二者都必须与匿名方法兼容。

下面的例子使用匿名方法编写了“内联”函数。匿名方法被作为Function委托类型而传递。

using System;

delegate double Function(double x);

class Test

{

static double[] Apply(double[] a ,Function f)

{

double[] result = new double[a.Length];

for(int i=0;i<a.Length;i++) result[i] = f(a[i]);

return result;

}

static double[] MultiplyAllBy(double[] a, double factor)