拥有一个具有方法的类,如下所示:
class Window {
public void Display(Button button) {
// ...
}
}
是否可以用另一个更宽泛的方法重载该方法,如下所示:
class WindowExtensions {
public void Display(this Window window, object o) {
Button button = BlahBlah(o);
window.Display(button);
}
}
当我尝试的时候发生的是我有无限的递归。有没有办法做到这一点呢?我希望仅当另一个方法无法调用时才调用扩展方法。
发布于 2010-01-22 23:07:04
让我们来看看规范。首先,我们必须了解方法调用的规则。粗略地说,您可以从尝试调用方法的实例所指示的类型开始。你沿着继承链向上走,寻找可访问的方法。然后,执行类型推断和重载解析规则,并在成功时调用该方法。只有当找不到这样的方法时,您才会尝试将该方法作为扩展方法处理。因此,从§7.5.5.2 (扩展方法调用)中可以看到,特别是加粗的语句:
其中一个表单的方法调用(§7.5.5.1)中的
expr.identifier()
expr.identifier(args)
expr.identifier<typeargs>()
expr.identifier<typeargs>(args)
如果调用的正常处理没有找到适用的方法,则会尝试将该构造作为扩展方法调用进行处理。
除此之外的规则有点复杂,但对于您向我们介绍的简单案例来说,它非常简单。如果没有适用的实例方法,则将调用扩展方法WindowExtensions.Display(Window, object)
。如果Window.Display
的参数是一个按钮,或者是可以隐式转换为按钮的,则可以使用instance方法。否则,将调用扩展方法(因为从object
派生的所有内容都可以隐式转换为object
)。
所以,除非你遗漏了一些重要的东西,否则你正在尝试做的事情将会奏效。
因此,考虑以下示例:
class Button { }
class Window {
public void Display(Button button) {
Console.WriteLine("Window.Button");
}
}
class NotAButtonButCanBeCastedToAButton {
public static implicit operator Button(
NotAButtonButCanBeCastedToAButton nab
) {
return new Button();
}
}
class NotAButtonButMustBeCastedToAButton {
public static explicit operator Button(
NotAButtonButMustBeCastedToAButton nab
) {
return new Button();
}
}
static class WindowExtensions {
public static void Display(this Window window, object o) {
Console.WriteLine("WindowExtensions.Button: {0}", o.ToString());
Button button = BlahBlah(o);
window.Display(button);
}
public static Button BlahBlah(object o) {
return new Button();
}
}
class Program {
static void Main(string[] args) {
Window w = new Window();
object o = new object();
w.Display(o); // extension
int i = 17;
w.Display(i); // extension
string s = "Hello, world!";
w.Display(s); // extension
Button b = new Button();
w.Display(b); // instance
var nab = new NotAButtonButCanBeCastedToAButton();
w.Display(b); // implicit cast so instance
var nabexplict = new NotAButtonButMustBeCastedToAButton();
w.Display(nabexplict); // only explicit cast so extension
w.Display((Button)nabexplict); // explictly casted so instance
}
}
这将打印出来
WindowExtensions.Button: System.Object
Window.Button
WindowExtensions.Button: 17
Window.Button
WindowExtensions.Button: Hello, world!
Window.Button
Window.Button
Window.Button
WindowExtensions.Button: NotAButtonButMustBeCastedToAButton
Window.Button
Window.Button
在控制台上。
发布于 2010-01-22 23:35:07
这是可能的,尽管您必须小心重载上的参数-避免使用object
类型通常是一个好主意,因为这通常会导致代码混乱。你可能会与C#挑选重载的有趣方式发生冲突。它将选择与可以隐式转换为的类型的“更接近”匹配,而不是具有精确匹配的“进一步”匹配(请参阅this question)。
Button myButton = // get button
Window currentWindow = // get window
// which method is called here?
currentWindow.Display( myButton );
您希望您的代码相当清晰,特别是当在一年左右的时间内返回到此代码时,将调用什么重载。
扩展方法提供了一种非常优雅的方式来扩展对象的功能。您可以添加它最初没有的行为。不过,您必须小心使用它们,因为它们很容易创建令人困惑的代码。最佳实践是避免已经使用的方法名称,即使它们是明确的重载,因为它们不会显示在intellisense中的类之后。
这里的问题似乎是扩展方法可以隐式地将按钮转换为对象,因此选择自己作为最佳匹配,而不是实际的显示方法。您可以将扩展方法作为普通静态调用显式调用,但不能强制它调用底层类的方法。
我将更改扩展方法的名称:
object somethingToMakeIntoAButton = // get object
Window currentWindow = // get window
// which method is called here?
currentWindow.DisplayButton( somethingToMakeIntoAButton );
然后..。
class WindowExtensions
{
public void DisplayButton(this Window window, object o)
{
Button button = BlahBlah(o);
// now it's clear to the C# compiler and human readers
// that you want the instance method
window.Display(button);
}
}
或者,如果扩展方法上的第二个参数是不能从Button
隐式转换为的类型(比如int
或string
),这种混淆也不会发生。
发布于 2010-01-22 23:10:51
这应该是可行的,编译器几乎总是会选择具有可接受签名的实例方法,而不是具有确切签名的扩展方法。
根据这个article
使用扩展转换的具有可接受签名的实例方法
几乎总是优先于具有精确签名匹配的扩展方法。如果这导致在您确实想使用扩展方法时绑定到实例方法,则可以使用共享方法调用约定显式调用扩展方法。当两种方法都不是很具体时,这也是消除两种方法歧义的方法。
您确定要显式传递一个Button吗?
或者void Display(Button button)
正在递归地调用自己?
https://stackoverflow.com/questions/2118064
复制相似问题