我想用自己的方法替换.NET或ASP MVC框架中包含的扩展方法。
实施例
public static string TextBox(this HtmlHelper htmlHelper, string name)
{
...
}
有可能吗?我无法使用override或new关键字。
答案 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);