C# 如何覆盖现有的扩展方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1451099/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 17:40:31  来源:igfitidea点击:

How to override an existing extension method

c#asp.net-mvc

提问by MiniScalope

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 关键字。

采纳答案by Eric Lippert

UPDATE: This question was the subject of my blog in December of 2013. Thanks for the great question!

更新:这个问题是我 2013 年 12 月博客的主题。谢谢你的好问题!



You can do this, in a sense. But I should start by talking briefly about the basic design principle of overload resolution in 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.

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

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. 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# 特别重视给定方法与调用站点的“接近度”。如果在基类中的适用方法或派生类中的新适用方法之间进行选择,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? and (2) did we find the extension method via usingor was it right there in the namespace? 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 usingdeclarations, to put the usingof the namespace that contains the desired static class closer than the other.

现在我们来回答你的问题。扩展方法的接近程度取决于(1)我们必须“离开”多少个命名空间?和 (2) 我们是通过using还是在命名空间中找到了扩展方法?因此,您可以通过更改静态扩展类出现在哪个命名空间中来影响重载解析,以将其放在更靠近调用站点的命名空间中。或者,您可以更改您的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 Blorpleget first go, then classes in FrobCo.MyExtensionNamespace, then classes in FrobCo, and then classes in BazCo.TheirExtensionNamespace.

现在,当重载决议去解析扩展方法调用时,Blorple首先是get 中的类FrobCo.MyExtensionNamespace,然后是 中的类,然后是 中的类FrobCo,然后是 中的类BazCo.TheirExtensionNamespace

Is that clear?

明白了吗?

回答by Andrew Hare

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

回答by James Black

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' 指令,而只需输入某些类的整个包名,这样就可以解决问题。

回答by Ondrej Svejdar

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”。

回答by marsze

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