2010-03-17 3 views
4

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

Пример:

/* 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

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

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

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

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

ExtensionMethods.Reverse(list); 

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

+0

Да, я имею в виду точно такую ​​же ситуацию. Если я правильно понимаю, самый правильный способ - использовать методы расширения, такие как статические методы, и не использовать методы расширений? Как я могу найти в обзоре кода, указанный код не использует методы расширения? – TcKs

+1

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

+0

@JacobM правильный. – JaredPar

1

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

+0

Да, это выглядит уродливо, но как насчет методов расширения от компонентов BCL/3rd party? – TcKs

+0

@TcKs - вы не можете полировать каждый код. Недостаточно времени. Пойдите с ответом @ Chad - напишите модульные тесты, которые покажут вам, был ли метод расширения заменен чем-то, что нарушает ваш код. – ChrisF

1

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

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

2

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

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

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