简体   繁体   English

如何覆盖现有的扩展方法

[英]How to override an existing extension method

I want to replace extension methods included in the .NET or ASP MVC framework by my own methods.我想用我自己的方法替换 .NET 或 ASP MVC 框架中包含的扩展方法。

Example例子

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

Is it possible?是否有可能? I can't use the override or new keyword.我不能使用 override 或 new 关键字。

UPDATE: This question was the subject of my blog in December of 2013 .更新:这个问题是我 2013 年 12 月博客的主题 Thanks for the great question!谢谢你的好问题!


You can do this, in a sense.从某种意义上说,你可以做到这一点。 But I should start by talking briefly about the basic design principle of overload resolution in C#.但我应该先简单谈谈 C# 中重载解析的基本设计原则。 All overload resolution is, of course, about taking a set of methods with the same name and choosing from that set the unique best member to call.当然,所有重载决议都是关于采用一组具有相同名称的方法并从该组中选择要调用的唯一最佳成员。

There are many factors involved in determining which is the "best" method;确定哪种方法是“最佳”方法涉及许多因素; different languages use a different "mixture" of factors to figure this out.不同的语言使用不同的“混合”因素来解决这个问题。 C# in particular heavily weights "closeness" of a given method to the call site. C# 特别重视给定方法与调用站点的“接近度”。 If given the choice between an applicable method in a base class or a new applicable method in a derived class, C# takes the one in the derived class because it is closer, even if the one in the base class is in every other way a better match.如果在基类中的适用方法或派生类中的新适用方法之间进行选择,C# 会采用派生类中的方法,因为它更接近,即使基类中的方法在其他方面都更好匹配。

And so we run down the list.所以我们列出了清单。 Derived classes are closer than base classes.派生类比基类更接近。 Inner classes are closer than outer classes.内部类比外部类更接近。 Methods in the class hierarchy are closer than extension methods.类层次结构中的方法比扩展方法更接近。

And now we come to your question.现在我们来回答你的问题。 The closeness of an extension method depends on (1) how many namespaces "out" did we have to go?扩展方法的接近程度取决于(1)我们必须“离开”多少个命名空间? and (2) did we find the extension method via using or was it right there in the namespace? (2) 我们是通过using找到了扩展方法还是它就在命名空间中? Therefore you can influence overload resolution by changing in what namespace your static extension class appears, to put it in a closer namespace to the call site.因此,您可以通过更改静态扩展类出现在哪个命名空间中来影响重载解析,将其放置在更接近调用站点的命名空间中。 Or, you can change your using declarations, to put the using of the namespace that contains the desired static class closer than the other.或者,您可以更改using声明,使包含所需静态类的命名空间的using比另一个更接近。

For example, if you have例如,如果你有

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

then it is ambiguous which is closer.那么哪个更接近是不明确的。 If you want to prioritize yours over theirs, you could choose to do this:如果你想优先考虑你的而不是他们的,你可以选择这样做:

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

And now when overload resolution goes to resolve the extension method call, classes in Blorple get first go, then classes in FrobCo.MyExtensionNamespace , then classes in FrobCo , and then classes in BazCo.TheirExtensionNamespace .而现在,当重载决议去解决扩展方法调用,在班Blorple得到第一次去,然后在课堂上FrobCo.MyExtensionNamespace ,然后在课堂上FrobCo ,然后班BazCo.TheirExtensionNamespace

Is that clear?明白了吗?

Extension methods cannot be overridden since they are not instance methods and they are not virtual.扩展方法不能被覆盖,因为它们不是实例方法,也不是虚拟方法。

The compiler will complain if you import both extension method classes via namespace as it will not know which method to call:如果您通过命名空间导入两个扩展方法类,编译器会抱怨,因为它不知道要调用哪个方法:

The call is ambiguous between the following methods or properties: ...以下方法或属性之间的调用不明确: ...

The only way around this is to call your extension method using normal static method syntax.解决此问题的唯一方法是使用正常的静态方法语法调用您的扩展方法。 So instead of this:所以而不是这个:

a.Foo();

you would have to do this:你必须这样做:

YourExtensionMethodClass.Foo(a);

Based on Eric premise (and the fact that views code is rendered into ASP namespace) you should be able to override it like this (at least it works for me in ASP.NET MVC4.0 Razor基于 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));
    }
  }
}

Note the namespace has to be "ASP".注意命名空间必须是“ASP”。

Extension methods are basically just static methods so I don't know how you can override them, but if you just put them in a different namespace then you can call yours instead of the one you want to replace.扩展方法基本上只是静态方法,所以我不知道如何覆盖它们,但是如果你只是将它们放在不同的命名空间中,那么你可以调用你的而不是你想要替换的。

But Matt Manela talks about how instance methods have precedence over extension methods: http://social.msdn.microsoft.com/forums/en-US/csharplanguage/thread/e42f1511-39e7-4fed-9e56-0cc19c00d33d但是 Matt Manela 谈到了实例方法如何优先于扩展方法: http ://social.msdn.microsoft.com/forums/en-US/csharplanguage/thread/e42f1511-39e7-4fed-9e56-0cc19c00d33d

For more ideas about extension methods you can look at http://gen5.info/q/2008/07/03/extension-methods-nulls-namespaces-and-precedence-in-c/有关扩展方法的更多想法,您可以查看http://gen5.info/q/2008/07/03/extension-methods-nulls-namespaces-and-precedence-in-c/

Edit: I forgot about the ambiguity issue, so your best bet is to try not including the extension methods you want to replace.编辑:我忘记了歧义问题,所以最好的办法是尽量不包括要替换的扩展方法。 So, you may need to not use the 'using' directive but just put in the entire package name of some classes, and this could solve the problem.因此,您可能不需要使用 'using' 指令,而只需输入某些类的整个包名称,这样就可以解决问题。

Overriding extension methods is not possible by design.设计上不可能覆盖扩展方法。 Here are 2 possible workarounds:以下是 2 种可能的解决方法:

1.) Use a more specific type 1.) 使用更具体的类型

This can only be used of course if the target type is more specific than that of the existing extension method.这当然只能在目标类型比现有扩展方法更具体的情况下使用。 Also, it might be necessary to do this for all applicable types.此外,可能需要对所有适用类型执行此操作。

// "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.) Add a dummy parameter 2.) 添加一个虚拟参数

Not as pretty.没那么漂亮。 Also, this will require you to modify existing calls to include the dummy argument.此外,这将要求您修改现有调用以包含虚拟参数。

// 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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM