Использование расширений: взвешивание плюсов и минусов - PullRequest
3 голосов
/ 16 февраля 2009

Недавно я задал вопрос о том, как убрать то, что я считал некрасивым кодом. Одной из рекомендаций было создание метода расширения, который выполнял бы желаемую функцию и возвращал обратно то, что я хотел. Моей первой мыслью было: «Отлично! Насколько классны расширения ... », но, немного подумав, я начинаю задумываться об использовании расширений ...

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

Возьмем, к примеру, мой предыдущий фрагмент кода вопросов:

if (entry.Properties["something"].Value != null)
  attribs.something = entry.Properties["something"].Value.ToString();

Теперь замените его на расширение:

public static class ObjectExtensions
{
    public static string NullSafeToString(this object obj)
    {
        return obj != null ? obj.ToString() : String.Empty;
    }
}

и вызов с использованием синтаксиса:

attribs.something = entry.Properties["something"].Value.NullSafeToString();

Определенно удобный способ, но действительно ли он стоит накладных расходов на другой объект класса? И что произойдет, если кто-то захочет повторно использовать мой фрагмент кода, но не поймет Расширение? Я мог бы так же легко использовать синтаксис с тем же результатом:

attribs.something = (entry.Properties["something"].Value ?? string.Empty).ToString()

Итак, я немного покопался и нашел пару статей, в которых рассказывалось о плюсах и минусах использования расширений. Для склонных взгляните на следующие ссылки:

MSDN: методы расширения

Методы расширения Best Practice

Методы расширения

Я не могу действительно решить, какой путь лучше. Пользовательские расширения, которые делают то, что я хочу, чтобы они делали, или более отображаемый код для выполнения той же задачи? Мне было бы очень интересно узнать, что «настоящие» разработчики думают об этой теме ...

Ответы [ 5 ]

17 голосов
/ 16 февраля 2009

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

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

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

РЕДАКТИРОВАТЬ: использование вами терминологии может слегка вводить вас в заблуждение. Нет такой вещи как «объект расширения» - есть только «методы расширения», и они должны существовать в статических типах. Поэтому вам может потребоваться ввести новый тип , но вы больше не создаете объекты .

6 голосов
/ 16 февраля 2009

[OP] Определенно удобный способ, но стоит ли накладные расходы другого объекта класса?

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

[OP] А что произойдет, если кто-то захочет повторно использовать мой фрагмент кода, но не поймет Расширение Объектов?

Тогда самое время их обучить :). Да, есть риск, что новому разработчику может не понравиться метод расширения для запуска. Но это вряд ли изолированная особенность. Он используется все больше и больше во всех примерах кода, которые я вижу внутри и в Интернете. Это то, что разработчик определенно стоит изучить. Я не думаю, что это вписывается в категорию «эзотерически ожидать, что люди узнают»

4 голосов
/ 16 февраля 2009

Единственная серьезная странность в методах расширения:

  1. Они не должны вызывать исключение нулевой ссылки, если левая сторона (объект, для которого вы вызываете метод) является нулевой.
    • иногда может быть полезным, но это противоречит ожиданиям, поэтому его следует использовать с особой осторожностью.
  2. Они недоступны из-за отражения классов / интерфейсов, к которым они применяются.
    • как правило, не проблема, но стоит иметь в виду.
  3. Конфликты имен с другими методами расширения включают длинную последовательность правил разрешения
    • если вам важно, последовательность должна предпочесть:
      1. Методы расширения, определенные внутри текущего модуля.
      2. Методы расширения, определенные внутри типов данных в текущем пространстве имен или в любом из его родителей, с дочерними пространствами имен, имеющими более высокий приоритет, чем родительские пространства имен.
      3. Методы расширения, определенные внутри импорта любого типа в текущем файле.
      4. Методы расширения, определенные внутри любого импорта пространства имен в текущем файле.
      5. Методы расширения, определенные внутри любого импорта типов уровня проекта.
      6. Методы расширения, определенные внутри любого импорта пространства имен уровня проекта.
2 голосов
/ 16 февраля 2009

[OP] А что произойдет, если кто-то захочет повторно использовать мой фрагмент кода, но не поймет Расширение Объектов?

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

Если ссылка на сборку метода расширения указана, она будет отображаться в intellisense, но она не будет упомянута в документации по объекту. Это может также вызвать некоторую путаницу.

Однако, как упоминал @JaredPar, методы расширения как техника используются все больше и больше, и я ожидаю, что большинство программистов на C # узнают о них. Таким образом, я не буду слишком беспокоиться о возможной путанице.

0 голосов
/ 16 января 2014

C # Extensions - это дополнительный «инструмент», предоставляемый .Net, чтобы помочь вам написать свой код немного лучше. Еще одним преимуществом является то, что они обрабатывают ноль. Хотя они кажутся очень удобными, я пытаюсь использовать их только в определенных случаях, которые действительно приведут в порядок мой код, потому что они не являются стандартными методами кодирования, и они немного отличаются от других классов, поскольку они должны быть в статических классах и статичны сами.

Допустим, их реализация немного неопрятна, но их использование сделано более аккуратным .

Также важно упомянуть, что они существуют только в C # и VB.Net (Java не имеет расширений). Другим важным фактом является то, что расширения не имеют приоритета над стандартными методами, а это означает, что если метод реализован в классе с тем же именем, что и метод расширения в том же классе, то первым будет вызван метод, а не метод. метод расширения.

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

1. Чтобы реализовать конкретные методы для универсальных классов: У меня есть универсальный тип, скажем, коллекция List<T>. Я хочу сделать метод, который применяется только к определенному виду списка. Скажем, метод, который создает объединение из списка строк, используя разделитель ("A", "B", "C", " sep " --> "A sep B sep C"):

public static string union(this List<string> stringList, String seperator)
{
   String unionString = "";
   foreach (string stringItem in stringList) {
      unionString += seperator + stringItem; }
   if (unionString != "") { 
      unionString = unionString.Substring(seperator.Length); }
   return unionString;
}

Если бы я не хотел использовать расширение, мне нужно было бы создать новый класс "StringCollection : List<string>" и реализовать там мой метод. Это в основном не проблема, и на самом деле это лучше в большинстве случаев, но не во всех случаях. Например, если во многих случаях вы получаете все свои данные в списках строк, вам не нужно конвертировать эти списки в StringCollections каждый раз, когда вы хотите использовать объединение, но вместо этого использовать расширение.

2. Для реализации методов, которые должны обрабатывать нуль: Мне нужен метод для преобразования объекта в строку без исключения в случае, если объект является нулевым

public static String toStringNullAllowed(this Object inputObject)
{
   if (inputObject == null) { return null; }
   return inputObject.ToString();
}

Если бы я не хотел использовать расширение, мне пришлось бы создать класс (возможно, статический), например StringConverter, который будет выполнять ту же работу, с большим количеством слов, чем простой myObject.toStringNullAllowed();

3. Чтобы расширить типы значений или запечатанные классы: Типы значений, такие как int, float, string и т. Д., А также запечатанные классы (классы, которые не могут быть унаследованы) не могут быть расширены посредством наследования. Ниже вы можете увидеть пример расширения целых чисел, которое можно преобразовать в x-разрядные строки (например, integer 34, digits 5 --> "00034"):

public static String toXDigit(this int inputInteger, int x)
{
   String xDigitNumber = inputInteger.ToString();
   while (xDigitNumber.Length < x) { xDigitNumber = "0" + xDigitNumber; }
   return xDigitNumber;
}

Опять же, альтернативным решением будет статический класс (например, набор инструментов), скажем, "Math".

  • В этом случае вы бы написали: Math.toXDigit(a, x);
  • При использовании метода расширения: a.toXDigit(x);

Метод расширения выглядит лучше и понятнее, как говорящий по-английски

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

...