Как переопределить существующий метод расширения
Я хочу заменить методы расширения, включенные в .NET или ASP MVC framework, моими собственными методами.
пример
public static string TextBox(this HtmlHelper htmlHelper, string name)
{
...
}
это возможно? Я не могу использовать переопределение или новое ключевое слово.
5 ответов:
обновление: этот вопрос был тема моего блога в декабре 2013 года. Спасибо за отличный вопрос!
вы можете сделать это, в каком-то смысле. Но я должен начать с краткого обсуждения основного принципа проектирования разрешения перегрузки в C#. Все разрешение перегрузки, конечно же, связано с использованием набора методов с тем же именем и выбором из этого набора уникального лучшего члена для вызова.
есть много факторов, участвующих в определение того, какой метод является" лучшим"; разные языки используют различную" смесь " факторов, чтобы понять это. C#, в частности, сильно взвешивает "близость" данного метода к сайту вызова. Если задан выбор между применимым методом в базовом классе или новым применимым методом в производном классе, C# берет тот, который находится в производном классе, потому что он ближе, даже если тот, который находится в базовом классе, во всех других отношениях лучше соответствует.
и так мы бежим вниз по списку. Полученный классы ближе, чем базовые классы. Внутренние классы ближе, чем внешние классы. Методов в иерархии классов ближе, чем методы расширения.
а теперь перейдем к вашему вопросу. Близость метода расширения зависит от (1) Сколько пространств имен "выход" нам нужно было сделать? и (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
.это понятно?
методы расширения нельзя переопределить, поскольку они не являются методами экземпляра и не являются виртуальными.
компилятор будет жаловаться, если вы импортируете оба класса метода расширения через пространство имен, поскольку он не будет знать, какой метод вызывать:
вызов неоднозначен между следующих методов или свойств: ...
единственный способ обойти это, чтобы вызвать метод расширения, используя обычный синтаксис статического метода. Так что вместо это:
a.Foo();
вы должны сделать это:
YourExtensionMethodClass.Foo(a);
методы расширения в основном просто статические методы, поэтому я не знаю, как вы можете их переопределить, но если вы просто поместите их в другое пространство имен, то вы можете вызвать свой вместо того, который вы хотите заменить.
но Мэтт Манела говорит о том, как методы экземпляра имеют приоритет над методами расширения: 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/
Edit: я забыл о проблеме неоднозначности, поэтому лучше всего попробовать не включать методы расширения, которые вы хотите заменить. Таким образом, вам может потребоваться не использовать директиву "using", а просто ввести полное имя пакета некоторых классов, и это может решить проблему.
основываясь на предпосылке 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".
В дополнение к существующим ответами, вы также можете "переопределить" методы расширения, изменив сигнатуру метода:
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);