【问题标题】:Is there any way in C# to override a class method with an extension method?C# 中有什么方法可以用扩展方法覆盖类方法吗?
【发布时间】:2010-10-28 07:47:37
【问题描述】:

在某些情况下,我想用扩展方法覆盖类中的方法。有没有办法在 C# 中做到这一点?

例如:

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

我想要这样做的一个例子是能够将字符串的散列存储到数据库中,并让所有使用字符串类散列的类(即字典等)使用相同的值。 ) 由于不保证内置的 .NET 散列算法从一个版本的框架兼容到下一个版本,我想用我自己的替换它。

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

我知道我可以通过对现有类进行子类化来做到这一点,但在很多情况下能够通过扩展来做到这一点会很方便。

【问题讨论】:

  • 由于字典是内存中的数据结构,散列算法是否从框架的一个版本更改为下一个版本有什么区别?如果框架版本发生变化,那么显然应用程序已经重新启动并且字典已经重新构建。

标签: c# extension-methods overriding


【解决方案1】:

没有;扩展方法永远不会优先于具有合适签名的实例方法,并且永远不会参与多态(GetHashCodevirtual 方法)。

【讨论】:

  • "...从不参与多态性(GetHashCode 是一个虚方法)" 您能否以另一种方式解释为什么扩展方法由于是虚的而从不参与多态性?我无法看到与这两个语句的关联。
  • @Alex 通过提到虚拟我只是在澄清多态的含义。在 GetHashCode 的几乎所有使用中,具体类型是未知的——所以多态性在起作用。因此,扩展方法即使在常规编译器中优先也无济于事。 OP 真正想要的是猴子补丁。哪个 c# 和 .net 不方便。
  • 这很可悲,在 C# 中有这么多误导性的无意义方法,例如.ToString() 在数组中。这绝对是一个必需的功能。
  • 那为什么没有编译器错误呢?例如。我打字。 namespace System { public static class guilty { public static string ToLower(this string s) { return s.ToUpper() + " I'm Malicious"; } } }
  • @LowLevel 你为什么会这样?
【解决方案2】:

如果该方法具有不同的签名,那么它可以完成 - 所以在你的情况下:否。

但否则你需要使用继承来做你正在寻找的东西。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      我找到了一种调用扩展方法的方法,该方法与类方法具有相同的签名,但是它似乎不是很优雅。在使用扩展方法时,我注意到一些未记录的行为。示例代码:

      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 委托,所以也许更有经验的人可以解决这个问题。

      【讨论】:

        猜你喜欢
        • 2011-07-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-10
        • 2018-07-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多