C#中有什么方法可以用扩展方法覆盖类方法?


98

有时候我想用扩展方法覆盖类中的方法。在C#中有什么方法可以做到这一点?

例如:

public static class StringExtension
{
    public static int GetHashCode(this string inStr)
    {
        return MyHash(inStr);
    }
}

我想要这样做的一种情况是能够将字符串的哈希存储到数据库中,并且使用该字符串类的哈希的所有类(例如Dictionary等)都使用相同的值。内置的.Net哈希算法不能保证从Framework的一个版本到下一个版本都兼容,我想用自己的版本替换它。

在其他情况下,我也想用扩展方法覆盖类方法,因此它不仅特定于字符串类或GetHashCode方法。

我知道我可以通过将现有类继承为子类来做到这一点,但是在很多情况下,能够通过扩展来做到这一点很方便。


由于字典是内存中的数据结构,因此哈希算法是否从框架的版本更改为下一个版本,有什么区别?如果框架版本更改,则很明显该应用程序已重新启动,并且字典已重新构建。
David Nelson,2009年

Answers:


91

没有; 扩展方法永远不会比具有适当签名的实例方法优先,并且永远不会参与多态性(GetHashCode是一种virtual方法)。


“ ...从不参与多态(GetHashCode是虚拟方法)”您能否以另一种方式解释为什么扩展方法由于是虚拟的而永远不会参与多态?我在查看与这两个语句的连接时遇到问题。
2014年

3
@Alex通过提及虚拟,我只是在阐明多态性的含义。实际上,在GetHashCode的所有使用中,具体类型都是未知的-多态性正在发挥作用。这样,即使扩展方法在常规编译器中具有优先权,扩展方法也无济于事。OP真正想要的是修补猴子。哪个c#和.net不方便使用。
Marc Gravell

4
令人遗憾的是,在C#中,有许多误导性的无意义方法,例如.ToString()在数组中。这绝对是必需的功能。
Hi-Angel

那为什么不出现编译器错误?例如,我键入。namespace System { public static class guilty { public static string ToLower(this string s) { return s.ToUpper() + " I'm Malicious"; } } }
LowLevel

@LowLevel为什么会呢?
Marc Gravell

7

如果该方法具有不同的签名,则可以完成-在您的情况下:否。

但是否则,您需要使用继承来完成您要寻找的事情。


2

据我所知答案是否定的,因为扩展方法不是实例,对我来说更像是一种智能感知功能,让您可以使用类的实例来调用静态方法。我认为您的问题的解决方案可以是拦截某个特定方法(例如GetHashCode())的执行并执行其他操作的拦截器。要使用这样的拦截器(如Castle Project提供的那样),应使用一个实例化所有对象。对象工厂(或Castle中的IoC容器),以便可以通过运行时生成的动态代理来拦截其接口。(Caslte还允许您拦截类的虚拟成员)


0

我已经找到了一种调用扩展方法的方法,该方法具有与类方法相同的签名,但是它看起来并不十分优雅。在使用扩展方法时,我注意到一些未记录的行为。样例代码:

public static class TestableExtensions
{
    public static string GetDesc(this ITestable ele)
    {
        return "Extension GetDesc";
    }

    public static void ValDesc(this ITestable ele, string choice)
    {
        if (choice == "ext def")
        {
            Console.WriteLine($"Base.Ext.Ext.GetDesc: {ele.GetDesc()}");
        }
        else if (choice == "ext base" && ele is BaseTest b)
        {
            Console.WriteLine($"Base.Ext.Base.GetDesc: {b.BaseFunc()}");
        }
    }

    public static string ExtFunc(this ITestable ele)
    {
        return ele.GetDesc();
    }

    public static void ExtAction(this ITestable ele, string choice)
    {
        ele.ValDesc(choice);
    }
}

public interface ITestable
{

}

public class BaseTest : ITestable
{
    public string GetDesc()
    {
        return "Base GetDesc";
    }

    public void ValDesc(string choice)
    {
        if (choice == "")
        {
            Console.WriteLine($"Base.GetDesc: {GetDesc()}");
        }
        else if (choice == "ext")
        {
            Console.WriteLine($"Base.Ext.GetDesc: {this.ExtFunc()}");
        }
        else
        {
            this.ExtAction(choice);
        }
    }

    public string BaseFunc()
    {
        return GetDesc();
    }
}

我注意到的是,如果我从扩展方法内部调用了第二个方法,即使存在一个也与签名匹配的类方法,它也会调用与签名匹配的扩展方法。例如,在上面的代码中,当我调用ExtFunc()时,依次调用ele.GetDesc(),我得到的返回字符串为“ Extension GetDesc”,而不是我们期望的字符串“ Base GetDesc”。

测试代码:

var bt = new BaseTest();
bt.ValDesc("");
//Output is Base.GetDesc: Base GetDesc
bt.ValDesc("ext");
//Output is Base.Ext.GetDesc: Extension GetDesc
bt.ValDesc("ext def");
//Output is Base.Ext.Ext.GetDesc: Extension GetDesc
bt.ValDesc("ext base");
//Output is Base.Ext.Base.GetDesc: Base GetDesc

这使您可以随意在类方法和扩展方法之间来回跳动,但需要添加重复的“传递”方法才能进入所需的“作用域”。我称此范围为缺少更好的词。希望有人能让我知道它的真正含义。

您可能已经猜到了我的“传递”方法名称,我也曾想过将委托传递给他们的想法,希望一个或两个方法可以充当具有相同签名的多个方法的传递。不幸的是,一旦委托被解压缩,就不会总是选择类方法而不是扩展方法,即使从另一个扩展方法内部也是如此。“范围”不再重要。尽管我并没有非常多地使用Action和Func代表,所以也许更有经验的人可以弄清楚这一点。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.