Когда вы используете методы расширения, доб. методы против наследования?
Мы начали использовать С# (.NET 3.0), и мне интересно, как вы, ребята, используете методы расширения? Когда вы их используете?
Кроме того, я был бы признателен, если бы вы также указали все темные предпосылки для их использования.
Ответы
Ответ 1
Методы расширения позволяют расширить существующие классы, не полагаясь на наследование или не изменить исходный код класса. Это означает, что если вы хотите добавить некоторые методы в существующий класс String, вы можете сделать это довольно легко. Вот пара правил, которые следует учитывать при принятии решения о том, следует ли использовать методы расширения:
-
Методы расширения не могут использоваться для переопределения существующих методов.
-
Метод расширения с тем же именем и сигнатурой, что и метод экземпляра, не будет называться
-
Концепция методов расширения не может применяться к полям, свойствам или событиям
-
Используйте методы расширения экономно.... чрезмерное использование может быть плохим!
Ответ 2
Время для использования методов расширения:
- когда вы не контролируете расширения типов
- где вы не хотите, чтобы разработчик предоставлял код, который может быть выполнен с использованием существующих методов
Пример второй точки; у вас может быть метод расширения на IList<T>
(например, Sort
), который может быть написан полностью с использованием существующих членов IList<T>
... так зачем заставлять кого-нибудь писать что-нибудь? Это базовый блок LINQ и позволил Microsoft обеспечить гораздо больше функциональности, не нарушая ничего.
Время < не использовать методы расширения:
- когда полиморфизм является критическим; вы не можете гарантировать, что ваш код будет версией, которая будет выполняться с использованием метода расширения, поскольку методы, непосредственно относящиеся к типу, имеют приоритет
- когда вам нужен доступ к частным/защищенным членам
Ответ 3
Я использую методы расширения, когда это имеет смысл. Если вы контролируете класс и его код, вам обычно не нужны методы расширения.
Если вы этого не сделаете, может быть полезным метод расширения.
Одно место, которое я часто использую методы расширения, - это перечисления [Flags]. Когда у вас есть перечисление на основе флага, существует довольно большое выражение, необходимое для определения того, имеет ли значение перечисления определенный набор флагов. И поэтому я строю следующий метод расширения всякий раз, когда я строю перечисление [Flags]:
[Flags]
public enum MyEnum
{
FlagA,
FlagB,
// etc.
}
public static class MyEnumExt
{
public static bool HasFlags(this MyEnum item, MyEnum query)
{
return ((item & query) == query);
}
}
Таким образом, мой код выглядит следующим образом:
MyEnum flags = MyEnum.FlagA;
if(flags.HasFlags(MyEnum.FlagA))
{
// handle FlagA
}
Вместо
MyEnum flags = MyEnum.FlagA;
if((flags & MyEnum.FlagA) == MyEnum.FlagA)
{
// handle FlagA
}
Ответ 4
Эта ссылка http://geekswithblogs.net/BlackRabbitCoder/archive/2010/04/26/c-extension-methods---to-extend-or-not-to-extend.aspx дает хорошие рекомендации о том, когда использовать методы расширения, а когда нет.
Чтобы процитировать эту статью:
Хороший метод расширения должен:
- Применить к любому возможному экземпляру типа, который он расширяет.
- Упростить логику и улучшить читаемость/ремонтопригодность.
- Применить к конкретному типу или интерфейсу.
- быть изолированным в пространстве имен, чтобы он не загрязнял IntelliSense.
Ответ 5
Честно говоря, я бы сказал, что легче сказать, когда это НЕ хорошая идея, тогда, когда это хорошая идея.
Я думаю, что основное преимущество методов расширения заключается в том, что они могут увеличить принятие вашего метода. Это связано с тем, что пользователю не нужно создавать экземпляр другого класса, чтобы использовать ваш метод, и intelisense будет рекламировать ваш метод, когда разработчик ищет методы для вашего класса. Это может быть важно, если вы пытаетесь заставить других разработчиков следовать новому стандарту в вашей компании.
При рассмотрении вопроса о том, следует ли создавать метод расширения, помните принципы SOLID.
S ingle Ответственность: - Вы почти всегда, по крайней мере, сгибаете принцип единой ответственности > с помощью метода расширения, потому что вы привязываетесь к тому, что уже есть > класс (который вы либо не контролируете, либо слишком боитесь касаться).
O ручка/принцип закрытия: - Методы расширения также не являются чрезмерными, что означает, что ваш метод не может быть "адекватно" открыт для расширения.
L принцип замены исков - Если у вас есть какая-то структура наследования, вы не сможете использовать методы расширения для вспомогательных типов.
I принцип сегрегации -Хотя вы можете "расширить" интерфейс, вы должны предоставить конкретную реализацию для этого расширения. Таким образом, вы не можете запрограммировать интерфейs > , который реализуется по-разному в другом контексте (например, Unit Test)
D принцип инверсии вершин - Если ваш код имеет какие-либо зависимости, как вы собираетесь раскрывать эти зависимости для заводов и модульных тестов (принцип инверсии зависимостей)?
Наконец, методы расширения - это просто статические методы, носящие новое платье. Таким образом, все трудности со статическими методами (например, безопасность потока, сборка мусора и т.д.) идут вместе с методами расширения при их реализации.
Итак, я долго и долго думал о написании метода в качестве расширения и пересмотреть с помощью factory и базового вспомогательного класса.
Если вы пишете метод расширения, сделайте его очень простым. Старайтесь избегать любых зависимостей (или передавать все ваши зависимости в качестве параметров). И будьте осторожны, как вы управляете памятью и блокируете ресурсы в многопоточной среде.