如何覆盖现有的扩展方法

时间:2009-09-20 14:12:00

标签: c# asp.net-mvc

我想用自己的方法替换.NET或ASP MVC框架中包含的扩展方法。

实施例

public static string TextBox(this HtmlHelper htmlHelper, string name)
{
   ...
}

有可能吗?我无法使用override或new关键字。

5 个答案:

答案 0 :(得分:98)

更新:这个问题是the subject of my blog in December of 2013。谢谢你提出的好问题!


从某种意义上说,你可以做到这一点。但我首先要简单谈谈C#中重载解析的基本设计原则。当然,所有重载决策都是关于采用一组具有相同名称的方法,并从该组中选择要调用的唯一最佳成员。

确定哪种是“最佳”方法涉及许多因素;不同的语言使用不同的“混合”因素来解决这个问题。 C#特别重视给定方法对呼叫站点的“接近度”。如果在基类中的适用方法或派生类中的新适用方法之间进行选择,C#将获取派生类中的一个,因为它更接近,即使基类中的那个更好,也是更好的比赛。

所以我们在列表中运行。派生类比基类更接近。内部类比外部类更接近。类层次结构中的方法比扩展方法更接近。

现在我们来回答您的问题。扩展方法的接近程度取决于(1)我们必须使用多少名称空间“out”? (2)我们是通过using找到扩展方法还是在命名空间中找到它?因此,您可以通过更改静态扩展类出现的命名空间来影响重载决策,将其放在调用站点的更近的命名空间中。或者,您可以更改using声明,将包含所需静态类的命名空间的using放在比另一个更近的位置。

例如,如果你有

namespace FrobCo.Blorble
{
  using BazCo.TheirExtensionNamespace;
  using FrobCo.MyExtensionNamespace;
  ... some extension method call
}

然后更加模糊不清。如果您想优先考虑他们的优先级,您可以选择这样做:

namespace FrobCo
{
  using BazCo.TheirExtensionNamespace;
  namespace Blorble
  {
    using FrobCo.MyExtensionNamespace;
    ... some extension method call
  }

现在,当重载决策用于解析扩展方法调用时,Blorple中的类首先进入,然后FrobCo.MyExtensionNamespace中的类,然后是FrobCo中的类,然后是BazCo.TheirExtensionNamespace中的类{1}}。

这是清楚的吗?

答案 1 :(得分:28)

扩展方法无法覆盖,因为它们不是实例方法,并且不是虚拟方法。

如果你通过命名空间导入两个扩展方法类,编译器会抱怨,因为它不知道要调用哪个方法:

  

以下方法或属性之间的调用不明确:...

解决此问题的唯一方法是使用常规静态方法语法调用扩展方法。所以不要这样:

a.Foo();

你必须这样做:

YourExtensionMethodClass.Foo(a);

答案 2 :(得分:4)

扩展方法基本上只是静态方法,因此我不知道如何覆盖它们,但是如果你只是将它们放在不同的命名空间中,那么你可以调用你的而不是你要替换的那个。

但Matt Manela谈到实例方法如何优先于扩展方法: http://social.msdn.microsoft.com/forums/en-US/csharplanguage/thread/e42f1511-39e7-4fed-9e56-0cc19c00d33d

有关扩展方法的更多想法,请查看http://gen5.info/q/2008/07/03/extension-methods-nulls-namespaces-and-precedence-in-c/

编辑:我忘记了歧义问题,所以最好的办法就是尽量不要包含你想要替换的扩展方法。因此,您可能不需要使用'using'指令,只需输入某些类的整个包名称,这可以解决问题。

答案 3 :(得分:3)

基于Eric的前提(以及视图代码呈现为ASP命名空间的事实),您应该能够像这样覆盖它(至少它在ASP.NET MVC4.0 Razor中适用于我

using System.Web.Mvc;

namespace ASP {
  public static class InputExtensionsOverride {
    public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name) {
      TagBuilder tagBuilder = new TagBuilder("input");
      tagBuilder.Attributes.Add("type", "text");
      tagBuilder.Attributes.Add("name", name);
      tagBuilder.Attributes.Add("crazy-override", "true");
      return new MvcHtmlString(tagBuilder.ToString(TagRenderMode.Normal));
    }
  }
}

请注意,命名空间必须是" ASP"。

答案 4 :(得分:1)

除现有答案外,您还可以"覆盖"通过改变方法签名的扩展方法:

1。)使用更具体的类型

如果目标类型比现有扩展方法更具体,则只能使用此选项。此外,可能需要对所有适用类型执行此操作。

// "override" the default Linq method by using a more specific type
public static bool Any<TSource>(this List<TSource> source)
{
    return Enumerable.Any(source);
}

// this will call YOUR extension method
new List<T>().Any();

2.)添加虚拟参数

不那么漂亮。此外,这将要求您修改现有调用以包含伪参数。

// this will be called instead of the default Linq method when "override" is specified
public static bool Any<TSource>(this IEnumerable<TSource> source, bool @override = true)
{
    return source.Any();
}

// this will call YOUR extension method
new List<T>().Any(true);
相关问题