C# 中的扩展方法重载有效吗?
拥有一个具有方法的类,如下所示:
class Window {
public void Display(Button button) {
// ...
}
}
是否可以用另一个更广泛的方法重载该方法,如下所示:
class WindowExtensions {
public void Display(this Window window, object o) {
Button button = BlahBlah(o);
window.Display(button);
}
}
当我尝试时发生的情况是我有无限递归。有办法让它发挥作用吗?我希望仅当无法调用其他方法时才调用扩展方法。
Having a class that has a method, like this:
class Window {
public void Display(Button button) {
// ...
}
}
is it possible to overload the method with another one that is more broad, like this:
class WindowExtensions {
public void Display(this Window window, object o) {
Button button = BlahBlah(o);
window.Display(button);
}
}
What happened when I tried is that I have infinite recursion. Is there a way to make that work? I want the extension method to be called only when the other method can't be called.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
让我们看看规格。首先,我们要了解方法调用的规则。粗略地说,您从尝试调用方法的实例指示的类型开始。您沿着继承链向上寻找可访问的方法。然后,您执行类型推断和重载解析规则,并在成功时调用该方法。仅当未找到此类方法时,您才会尝试将该方法作为扩展方法进行处理。因此,从 §7.5.5.2(扩展方法调用)中可以看到,特别是粗体语句:
除此之外的规则有点复杂,但对于您向我们展示的简单情况来说,它非常简单。如果没有适用的实例方法,则将调用扩展方法
WindowExtensions.Display(Window, object)
。如果Window.Display
的参数是按钮或者可以隐式转换为按钮,则实例方法适用。否则,将调用扩展方法(因为从object
派生的所有内容都可以隐式转换为object
)。因此,除非您遗漏了重要的部分,否则您尝试做的事情将会起作用。
因此,请考虑以下示例:
这将打印
在控制台上。
Let's go to the specification. First, we have to understand the rules for method invocations. Roughly, you start with the type indicated by the instance you are trying to invoke a method on. You walk up the inheritance chain looking for an accessible method. Then you do your type inference and overload resolution rules and invoke the method if that succeeds. Only if no such method is found do you try to process the method as an extension method. So from §7.5.5.2 (Extension method invocations) see, in particular, the bolded statement:
The rules beyond that get a little complicated, but for the simple case you've presented to us it's quite simple. If there is no applicable instance method then the extension method
WindowExtensions.Display(Window, object)
will be invoked. The instance method is applicable if the parameter toWindow.Display
is a button or is implicitly castable to a button. Otherwise, the extension method will be invoked (because everything that derives fromobject
is implicitly castable to anobject
).So, unless there is an important bit that you are leaving out, what you are trying to do will work.
So, consider the following example:
This will print
on the console.
这是可能的,尽管您必须小心重载的参数 - 通常最好避免
object
类型,因为这通常会导致代码混乱。您可能会犯下 C# 选择重载的有趣方式。它将选择一种“更接近”的匹配类型,该类型可以隐式转换为具有完全匹配的“进一步”匹配类型(请参阅此问题)。您希望您的代码相当清晰,尤其是在一年左右返回此代码时,正在调用什么重载。
扩展方法提供了一种非常优雅的方式来扩展对象的功能。您可以添加它最初没有的行为。不过,您必须小心使用它们,因为它们很容易创建令人困惑的代码。最佳实践是避免已使用的方法名称,即使它们是明显的重载,因为它们不会出现在智能感知中的类之后。
这里的问题似乎是扩展方法可以隐式地将按钮转换为对象,因此选择自身作为最佳匹配,而不是实际的显示方法。您可以像普通静态调用一样显式调用扩展方法,但不能强制它调用基础类的方法。
我会更改扩展方法的名称:
然后...
或者,如果扩展方法上的第二个参数是无法从
Button
隐式转换为的类型(例如int< /code> 或
string
)这种混乱也不会发生。It is possible, although you have to be careful with the parameters on overloads - it's usually a good idea to avoid
object
types as that often causes confusing code. You can fall foul of the funny way C# picks overloads. It will choose a 'closer' match with types that can be implicitly cast to over a 'further' one that has exact matches (see this question).You want your code to be fairly clear, especially when returning to this code in a year or so, what overload is being called.
Extension methods provide a really elegant way to expand the functionality of objects. You can add behaviour that it didn't have originally. You have to be careful with them though, as they're very prone to creating confusing code. It's best practice to avoid method names already used, even if they are clear overloads, as they don't show up after the class in intellisense.
Here the problem appears to be that the extension method can implicitly convert your button to an object, and so picks itself as the best match, instead of the actual display method. You can explicitly call the extension method as a normal static call, but you can't force it to call the underlying class's method.
I would change the name of the extension method:
Then...
Alternatively if the second parameter on the extension method was a type that couldn't be implicitly converted to from
Button
(sayint
orstring
) this confusion wouldn't happen either.这应该可行,编译器几乎总是会选择具有可接受签名的实例方法,而不是具有确切签名的扩展方法。
根据这篇文章:
您确定要明确传递一个按钮吗?
或者是
void Display(Button button)
递归调用自身?That should work, the compiler will almost always pick an instance method with an acceptable signature over an extension method with the exact signature.
According to this article:
Are you sure you're explicitly passing a Button?
Or is
void Display(Button button)
recursively calling itself?嗯,我相信这有点棘手。如果您将
Button
作为方法参数传递:那么就有合适的类方法,该方法始终优先于扩展方法。
但是,如果您传递的对象不是
Button
,则没有合适的类方法,并且将调用扩展方法。因此,从我看来,您的示例应该可以正常工作,并且扩展方法将调用
Window
实例上的Display
方法。无限循环可能是由其他一些代码引起的。示例中包含
Display
方法的Window
类和作为扩展方法参数的Window
类是否有可能实际上是两个不同的类?Well, I believe this is a little tricky. If you pass
Button
as a method parameter:then there is suitable class method which always takes precedence over the extension method.
But if you pass object that is not
Button
then there is no suitable class method and extension method will be invoked.So, from what I see, your example should work correctly and extension method will call
Display
method on theWindow
instance. The infinite loop might be caused by some other code.Is there any chance that
Window
class containingDisplay
method in your example andWindow
class that is a parameter to the extension method are actually two different classes?这是不可能的(另请参阅 Monkeypatching For Humans) - 也许使用 DLR 和
method_missing
。It's not possible (also see Monkeypatching For Humans) - maybe with DLR and
method_missing
.