Я не нашел простого пути, как с помощью Visual Studio и ReSharperа ввести синоним в уже написанном классе. В связи с чем я решил реализовать свое дополнение к ReSharper которое позволило бы решить эту проблему. В этой статье я бы хотел рассказать о подводных камнях, с которыми пришлось столкнуться, реализуя, на первый взгляд, такой простой рефакторинг. (исходный код и реализация в конце статьи)
Итак. Я хотел получить код, который поможет сделать что-то такое:
|
|
К моей радости, объектная модель ReSharper-a позволила легко получить для каждого типа его FQN(fully qualified name), т.е. в примере мы точно можем сказать, что класс MyTest находится в пространстве имен b.b2 и его FQN b.b2.MyTest
Первая формулировка алгоритма может выглядеть следующим образом:
Если мы хотим ввести псевдоним x для пространства имен b.b2, то следует для всех используемых типов в части кода, на который распространяется данный using: если использование типа лежит в пространстве b.b2 и его запись в коде не использует FQN, то необходимо добавить префикс x.
Возникает проблема — мы забыли про Extension методы. Extension методы могут вызваны только в том случае, если в файл явно импортировано пространство имен. Когда мы заменяем прямой импорт на синоним, то компилятор не может узнать, какой метод ему нужно вызывать. Проблема легко решается — extension методы из классов, которые находятся в пространстве имен b.b2. необходимо вызывать как члены статического класса, в котором они лежат.
было | будет | надо |
---|---|---|
|
|
|
Далее идут грабли, на которые я наткнулся, пытаясь запустить рефакторинг на тестовых файлах. Например, нельзя просто сравнивать на то, используется FQN или нет
было | будет | надо |
---|---|---|
|
|
|
Такая ошибка возникнет, т.к. FQN для MyTest это b.b2.MyTest. Наш рефакторинг, зная эту информацию, добавляет суффикс. Эту ошибку можно исправить, если вместо операции добавления префикса использовать полное замещение использование типа на x.[ShortTypeName].
Конфликты имен
Отдельно стоит проблема, когда значения x будет конфликтовать с уже определенным чем-либо
|
|
Правильный рефакторинг должен проверить свой результат на наличие таких ошибок. В реализации я игнорировал эту проблему, переложив это на плечи разработчика. К тому же, в случае конфликтов, компилятор об этом обязательно сообщит.
query-expression
Остается еще одна проблема. Что если пользователь захочет ввести синоним для пространства имен System.Linq (и ему подобных). В случае, если используются Extension методы, то наш алгоритм справится замечательно. Но если используется query-expression то ничего хорошего не выйдет
было | будет |
---|---|
|
|
Правильный рефакторинг должен раскрыть query expression в цепочку вызовов статических методов (что, собственно и делает компилятор) и получить примерно такой результат.
var query = aaa.Enumerable.Select( aaa.Enumerable.Where( aaa.Enumerable.Where( aaa.Enumerable.Join( svcContext.ContactSet, svcContext.AccountSet, c => c.ContactId, a => a.PrimaryContactId.Id, (c, a) => new { c, a }), @t => @t.a.Name.Contains("Contoso")), @t => @t.c.LastName.Contains("Smith")), @t => new { account_name = @t.a.Name, contact_name = @t.c.LastName });
Не думаю, что разработчик будет рад, если рефакторинг так жестоко поиздевается над его кодом. Поэтому в своей реализации я просто не разрешаю вводить синоним для этих пространств имен.
Несколько слов о реализации
В сети достаточно немного информации о том, как писать плагины для решарпера. Основной источник информации — декомпилятор. Немногим помогает SDK, в котором имеется несколько примеров написания расширений (но на рефакторинг — только один).
Исходные коды доступны на GitHub, но я крайне не рекомендую использовать их для изучения внутренней кухни ReSharper-а.
Результат меня устроил. Надеюсь кому-нибудь пригодиться.
resharper-plugins.jetbrains.com/packages/IntroduceNsAlias/
github.com/ulex/IntroduceNsAlias
Дополнительная информация для плагинописателей ReSharper-a:
• декомпилятор
• confluence.jetbrains.com/display/NETCOM/ReSharper+Plugin+Development
• tv.jetbrains.net/videocontent/getting-started-with-resharper-sdk
ссылка на оригинал статьи http://habrahabr.ru/post/191604/
Добавить комментарий