Методы расширений и прямая совместимость исходного кода - PullRequest
4 голосов
/ 17 марта 2010

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

Пример:

/* the code written in 17. March 2010 */
public class MySpecialList : IList<MySpecialClass> {
    // ... implementation
}
// ... somewhere elsewhere ...
MySpecialList list = GetMySpecialList(); // returns list of special classes
var reversedList = list.Reverse().ToList(); // .Reverse() is extension method
/* now the "list" is unchanged and "reveresedList" has same items in reversed order */

/* --- in future the interface of MySpecialList will be changed because of reason XYZ*/

/* the code written in some future */
public class MySpecialList : IList<MySpecialClass> {
    // ... implementation
    public MySpecialList Reverse() {
        // reverse order of items in this collection
        return this;
    }
}
// ... somewhere elsewhere ...
MySpecialList list = GetMySpecialList(); // returns list of special classes
var reversedList = list.Reverse().ToList(); // .Reverse() was extension method but now is instance method and do something else !
/* now the "list" is reversed order of items and "reveresedList" has same items lake in "list" */

Мой вопрос: есть ли способ предотвратить это дело (я их не нашел)? Если сейчас есть способ, как предотвратить это, есть ли способ, как найти возможные проблемы, как это? Если сейчас есть способ, как найти возможные проблемы, я должен запретить использование методов расширения?

Спасибо.

EDIT:

Ваш ответ был полезен. Могу ли я найти, где в коде используются методы расширения? И / или я могу найти, где в коде используются методы экземпляра, но существует метод расширения с такой же сигнатурой?

Ответы [ 4 ]

4 голосов
/ 17 марта 2010

Похоже, вы описываете следующую ситуацию

  1. В V1 вашего продукта MySpecialList не имеет метода Reverse, поэтому все вызовы Reverse связаны с методом расширения с тем же именем
  2. В V2 вашего продукта MySpecialList получает метод Reverse, и теперь все предыдущие привязки к методу расширения связываются с методом экземпляра.

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

Единственный способ предотвратить это на 100% - вызвать методы расширения как статические методы. Например

ExtensionMethods.Reverse(list);

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

2 голосов
/ 17 марта 2010

И вот почему мы пишем модульные тесты.

Сначала напишите методы расширения. Назовите их точно. Так что однажды, если метод расширения реализован как реальный метод в классе с тем же именем, есть хороший шанс, он делает то же самое, что и ваш метод расширения, и ничего не ломается.

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

1 голос
/ 17 марта 2010

В этом конкретном случае я бы поспорил, что метод расширения, который возвращает новый, перевернутый список (а не переворачивает список на месте), не должен называться «Обратным», а должен быть getReversedList () или что-то подобное.

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

1 голос
/ 17 марта 2010

Единственный способ гарантировать это - дать вашим методам расширения уникальные имена. Это может быть так же просто, как префикс метода с вашими инициалами. Это выглядит ужасно, я знаю, но должно работать 99,9% времени.

...