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

Переопределение методов расширения

Я думал об использовании методов расширения в качестве замены абстрактного базового класса. Методы расширения могут предоставлять функции по умолчанию и могут быть "переопределены", помещая метод одной и той же сигнатуры в производный класс.

По какой-то причине я не должен этого делать?

Кроме того, если у меня есть два метода расширения с одной и той же сигнатурой, какой из них используется? Есть ли способ установить приоритет?

4b9b3361

Ответ 1

Я согласен с Майклом. Базовые классы должны содержать всю базовую функциональность. Методы расширения должны, очевидно, расширять базовую функциональность. В динамических языках, таких как Ruby, часто типично использовать методы расширения для обеспечения функциональности добавления вместо использования подклассов. В основном, существуют методы расширения, чтобы заменить использование подклассов, а не заменять базовые классы.

Единственное исключение из этого, что я видел, - это если у вас есть несколько типов, которые имеют разные иерархии классов (например, элементы управления winform), вы можете сделать подкласс каждого из них, что все реализации и интерфейса, а затем расширить этот интерфейс, тем самым давая "базовая" функциональность для группы различных элементов управления, без расширения всего, как Control или Object.

Изменить: ответ на второй вопрос

Я думаю, что компилятор поймает это для вас.

Ответ 2

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

Моя точка зрения заключается в том, что вы можете это не значит, что вам нужно. Часто лучше всего придерживаться хорошего старомодного ООП и использовать более новые языковые функции, когда простое старое программирование OO не дает вам разумного решения.

Ответ 3

Это определенно плохая идея. Методы расширения привязаны статически, что означает, что, если вы не вызываете переопределение объекта, чей тип времени компиляции является подтипом, вы все равно продолжаете называть метод расширения. Прошлый полиморфизм. Эта страница содержит хорошее обсуждение опасностей методов расширения.

Ответ 4

Все ответы здесь указаны "вы не можете", что верно, насколько это возможно. Большинство добавлено, и вы не должны ". Я хотел бы сделать так, чтобы вы были в состоянии - небольшой комфорт, как это может быть.

Возьмите пример болезненного реального мира: если вам очень жаль, что вы используете новую структуру MVC, а ваш код просмотра использует какой-либо метод расширения HtmlHelper повсюду, и вы хотите переопределить его поведение по умолчанию... что то?

Вы SOL, это что. Даже если вы сделали "вещь ООП" - из HtmlHelper, измените класс базового представления, заменив экземпляр объекта "Html" экземпляром вашего DerivedHtmlHelper и определите в нем явный метод "Foo" - даже если вы сделали все что при вызове 'Html.Foo' будет еще вызывать оригинальный метод расширения, а не метод.

Это удивительно! В конце концов, методы расширения, как ожидается, будут применяться, если объект еще не имеет метода! Что здесь происходит?

Ну, это потому, что методы расширения являются статическими. То есть, увидев "Html.Foo", компилятор просматривает тип static типа "Html". Если он имеет метод "Foo", он называется как обычно. В противном случае, если "SomeClass" предоставляет метод расширения Foo, он преобразует выражение в "SomeClass.Foo(Html)".

Что вы ожидаете, так это то, что компилятор рассмотрит тип динамический объекта. То есть, сгенерированный (псевдо) код будет читать "Html.HasMethod(" Foo ")? Html.Foo(): SomeClass.Foo(Html) '.

Это, конечно, повлечет за собой затраты на использование отражения в каждом вызове метода расширения. Таким образом, вы можете ожидать, что вы можете написать что-то вроде "static void Foo ( virtual this HtmlHelper html)", чтобы явно запросить компилятор вставить проверку времени выполнения. Назовите это "метод виртуального расширения".

Однако в ограниченном бюджете и бесконечной мудрости разработчики языка С# пошли только с более эффективной и более ограниченной альтернативой. Что оставляет меня еще SOL, когда мне нужно переопределить поведение по умолчанию HtmlHelper: - (

Ответ 5

Это семантически разные операции. Например, полиморфизм может работать не так, как с абстрактным базовым классом.

Как правило, используйте любой инструмент языка, для которого он предназначен. Методы расширения не являются заменой для наследования, это метод расширения функциональности класса с использованием (обычно) уже видимого интерфейса.

Ответ 6

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

MyExtensions.AMethod(myObj)

или

myObj.AMethod()

Второй - это просто синтаксический сахар для первого.

То, что вы предлагаете, противоречит духу языковой функции. Методы расширения явно не объектно-ориентированные. Тем не менее вы пытаетесь достичь объектно-ориентированной техники. Не используйте для этого методы расширения.

Ответ 7

Вы можете захотеть рассмотреть вышеописанный полиморфизм, если вам не нужны уникальные производные классы.

Несколько общих рекомендаций из этой статьи MSDN :

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

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

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

Хотя это не полиморфизм, вы можете не захотеть использовать полиморфизм, поскольку для реализации их переопределений потребуются все производные классы.

Другими словами, если у вас есть производные с конкретной логикой варианта, используйте полиморфизм. Если вам просто нужны пользовательские функции в конкретных случаях, которые в противном случае могут быть запутанными или обременительными, методы расширения не являются плохой идеей.

Также см. ответ JoshRivers, в котором показано, как переопределить метод расширения, используя пространство имен внутри класса в отдельном пространстве имен.

Ответ 8

Сначала было бы неплохо проверить [новый] как модификатор метода - он должен давать одинаковое разделение функций и методов, но с гораздо меньшим количеством хлопот.

Если после этого вы все еще размышляете об одном и том же трюке, вот основные варианты, которые следует учитывать - никакой идеологии не требуется, просто перечисляя apsects, вам нужно знать:-)

Для начала вам, возможно, придется ограничить все виды использования шаблонами функций, чтобы гарантировать детерминированный выбор функции, поскольку этот трюк создаст ситуацию, которая является точной инверсией той, в которой CLR гарантирует детерминированный приоритет привязки к границам вызовов функций.

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

Если вы нацелены на преобразование абстрактного класса в конкретный, устраняя все виртуальные методы по первичным причинам и используя конкретный базовый класс и все производные исключительно в шаблонах (возможно, даже преобразование в структуры), тогда этот трюк может купить вам какой-то перфект. Вам просто нужно знать, что вы не сможете вернуться к использованию интерфейса без рефакторинга. Вам также нужно будет проверить порядок вызова в не-шаблонных функциях с базовым классом в подписи и быть особенно осторожным, если у вас есть несколько dll-s. Попробуйте то же самое с оператором [new] и посмотрите, работает ли он лучше.

Кроме того, вам придется писать отдельную реализацию метода для каждого производного класса, даже если они имеют точно такую ​​же функциональность, поэтому, если у вас много производных классов, вы смотрите на много дублирования кода. Эта часть поразит вас, даже если вы используете [новый], если не вернетесь к виртуальным методам и не добавите еще один слой в наследство, что, конечно же, отменит все перфомансы.