首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C#中的扩展方法重载,它能工作吗?

C#中的扩展方法重载,它能工作吗?
EN

Stack Overflow用户
提问于 2010-01-22 22:58:26
回答 5查看 4.4K关注 0票数 7

拥有一个具有方法的类,如下所示:

代码语言:javascript
运行
复制
class Window {
    public void Display(Button button) {
        // ...
    }
}

是否可以用另一个更宽泛的方法重载该方法,如下所示:

代码语言:javascript
运行
复制
class WindowExtensions {
    public void Display(this Window window, object o) {
        Button button = BlahBlah(o);
        window.Display(button);
    }
}

当我尝试的时候发生的是我有无限的递归。有没有办法做到这一点呢?我希望仅当另一个方法无法调用时才调用扩展方法。

EN

回答 5

Stack Overflow用户

发布于 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)。

所以,除非你遗漏了一些重要的东西,否则你正在尝试做的事情将会奏效。

因此,考虑以下示例:

代码语言:javascript
运行
复制
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
    }
}

这将打印出来

代码语言:javascript
运行
复制
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

在控制台上。

票数 9
EN

Stack Overflow用户

发布于 2010-01-22 23:35:07

这是可能的,尽管您必须小心重载上的参数-避免使用object类型通常是一个好主意,因为这通常会导致代码混乱。你可能会与C#挑选重载的有趣方式发生冲突。它将选择与可以隐式转换为的类型的“更接近”匹配,而不是具有精确匹配的“进一步”匹配(请参阅this question)。

代码语言:javascript
运行
复制
Button myButton = // get button
Window currentWindow = // get window

// which method is called here?
currentWindow.Display( myButton );

您希望您的代码相当清晰,特别是当在一年左右的时间内返回到此代码时,将调用什么重载。

扩展方法提供了一种非常优雅的方式来扩展对象的功能。您可以添加它最初没有的行为。不过,您必须小心使用它们,因为它们很容易创建令人困惑的代码。最佳实践是避免已经使用的方法名称,即使它们是明确的重载,因为它们不会显示在intellisense中的类之后。

这里的问题似乎是扩展方法可以隐式地将按钮转换为对象,因此选择自己作为最佳匹配,而不是实际的显示方法。您可以将扩展方法作为普通静态调用显式调用,但不能强制它调用底层类的方法。

我将更改扩展方法的名称:

代码语言:javascript
运行
复制
object somethingToMakeIntoAButton = // get object
Window currentWindow = // get window

// which method is called here?
currentWindow.DisplayButton( somethingToMakeIntoAButton );

然后..。

代码语言:javascript
运行
复制
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隐式转换为的类型(比如intstring),这种混淆也不会发生。

票数 2
EN

Stack Overflow用户

发布于 2010-01-22 23:10:51

这应该是可行的,编译器几乎总是会选择具有可接受签名的实例方法,而不是具有确切签名的扩展方法。

根据这个article

使用扩展转换的具有可接受签名的实例方法

几乎总是优先于具有精确签名匹配的扩展方法。如果这导致在您确实想使用扩展方法时绑定到实例方法,则可以使用共享方法调用约定显式调用扩展方法。当两种方法都不是很具体时,这也是消除两种方法歧义的方法。

您确定要显式传递一个Button吗?

或者void Display(Button button)正在递归地调用自己?

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2118064

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档