Подтвердить что ты не робот

Как переопределить существующий метод расширения

Я хочу заменить методы расширения, включенные в среду .NET или ASP MVC, своими собственными методами.

Пример

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

Возможно ли это? Я не могу использовать ключевое слово override или new.

4b9b3361

Ответ 1

UPDATE: этот вопрос был тема моего блога в декабре 2013 года. Спасибо за отличный вопрос!


Вы можете сделать это, в некотором смысле. Но я должен начать с краткого обсуждения базового принципа проектирования разрешения перегрузки в С#. Разумеется, все разрешения перегрузки включают набор методов с тем же именем и выбор из этого набора уникального лучшего участника для вызова.

Существует множество факторов, определяющих, что является "лучшим" методом; на разных языках используется другая "смесь" факторов, чтобы понять это. С#, в частности, сильно "близость" данного метода к сайту вызова. Если задан выбор между применимым методом в базовом классе или новым применимым методом в производном классе, С# берет один из производного класса, потому что он ближе, даже если тот, который находится в базовом классе, в любом другом случае лучше совпадение.

Итак, мы закончили список. Производные классы ближе, чем базовые классы. Внутренние классы ближе, чем внешние классы. Методы в иерархии классов ближе, чем методы расширения.

И теперь мы подошли к вашему вопросу. Близость метода расширения зависит от (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 получают сначала go, затем классы в FrobCo.MyExtensionNamespace, затем классы в FrobCo, а затем классы в BazCo.TheirExtensionNamespace.

Это ясно?

Ответ 2

Методы расширения нельзя переопределить, поскольку они не являются методами экземпляра, и они не являются виртуальными.

Компилятор будет жаловаться, если вы импортируете оба класса метода расширения через пространство имен, поскольку он не будет знать, какой метод вызывать:

Вызов неоднозначен между следующими методами или свойствами:...

Единственный способ обойти это - вызвать метод расширения с использованием стандартного синтаксиса статического метода. Поэтому вместо этого:

a.Foo();

вам нужно будет сделать это:

YourExtensionMethodClass.Foo(a);

Ответ 3

Методы расширения - это в основном просто статические методы, поэтому я не знаю, как их можно переопределить, но если вы просто поместите их в другое пространство имен, вы можете позвонить вместо них, вместо того, который хотите заменить.

Но Мэтт Манела рассказывает о том, как методы экземпляра имеют приоритет над методами расширения: 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", а просто указать полное имя пакета для некоторых классов, и это может решить проблему.

Ответ 4

Основываясь на предположении Эрика (и тот факт, что код представления отображается в пространстве имен 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".