Как стать автором
Обновить
1
0
Александр Шведов @ControlFlow

Пользователь

Отправить сообщение
Ну камон, это самая известная проблема StreamReader'ов, что он диспоузит underlying стрим. Конечно же люди юзают несколько ридеров/райтеров с одним стримом. «Никуда не передаются» — любой вызов instance-члена на таком объекте (иначе зачем он вообще?) — это передача этого ссылки на этот объект через `this`, что может продлить его время жизни. Атрибутами пользуются минимум пользователей, нет возможности автоматизировать проверку применений таких атрибутов. Ну правда, умножьте ваш взгляд на проблему IDisposable на порядок и вы приблизитись к истине. Или напишите плагином и покажите нам как надо.

У алиасов только одно нормальное применение — разрешать конфликты коротких имен. Остальное — это извращения «структурно-типизированных» программистов, экономящих декларации типов.

Что не так с приватными сеттерами? Чем акцессор отличается от члена класса, которому можно «уменьшить» уровень доступа?

Убивание `else` снижает уровень нестинга — мне тоже несколько лет потребовалось чтобы понять и принять, что это все же во имя добра. Может быть и к вам придет.

Форматирование по умолчанию — если бы вы у нас работали, то конечно бы смогли угадать такое количество столбцов в документе, которое устраивалось бы абсолютно всех. Но вы почему-то все еще не работаете у нас :(

«Import static members» — это не суджешшен, не инспекция, это контекстное действие. Маленький рефакторинг. Структурное редактирование кода. Оно делает что делает не потому что оно может сделать ваш код лучше или нет. Оно лишь автоматизирует ваше намерение сделать что-то.

Комментарии уже много лет не знаем как научиться сохранять при произвольных трансформациях, но вы конечно бы решили эту проблему. Для всех тысяч экшенов и фиксов. И рефакторингов.

Нет, нельзя переделать на «BindingPathAttribute». Чтобы этот атрибут прочитать, нужна семантическая модель. А сама поддержка property-path работает гораздо раньше, на этапе парсинга. Но вы конечно придумали бы как все отрефакторить на двухфазное построение синтаксических/семантических моделей.

Эх
Пока у вас из реальных успехов только демотивация разработчиков :)

Серьезно, кажется вы знаете лучше нас обо всех проблемах и как варить IDE, почему бы не превратить странное настойчивое желание поныть в комментариях к каждому официальному блогпосту в работу на благо таких же как вы программистов?
Мне всегда нравится, что программисты вне IDE-индустрии всегда намного лучше знают как делать IDE, вот только работают почему-то по другую сторону. IDisposable инспекции знают все как сделать, а кто потом будет тысячи false positive разбирать и объяснять юзерам почему мы задиспузили их Stream предложив задиспоузить локальный StreamReader? Алиасы извлекать чтобы люди по всему миру делали больше 'using UsersList = Dictionary<int, MultiDictionary<User, ExtraData<Dictionary<int, string>, decimal>>>;', а потом через релизы помогать людям избавлять их через инлайн или сохранять во всех рефакторингах (что КРАЙНЕ сложно)? И как поддерживать любые кастомные биндинги в XAML (в котором просто дохрена всего работает в сугубо рантайме) с кастомно вычисляемыми дата-контекстами тоже все знают… эх

Что не так с нугетом? Nuget-плагин давно включен в поставку, из фич добавляем ссылки на целые пакеты, а не бинарные сборки. Не поддерживаем сборки из nuget-пакетов как единое целое в фиче «Remove unreferenced code», что-то еще?
В ReSharper мы поддерживали синтаксис `() => e` (и конечно `() => { }`) с самого начала поддержки TypeScript, потому что он был в 1.0. Даже недокументированный синтаксис `function() => e` хотели уже поддержать, но его дропнули до релиза.

Разработчиком тулзов сложнее/дороже писать половину компилятора и справляться с расхождениями, но ни у кого не возникает почему-то мыслей, что компиляторы:

1)… не всегда обладают необходимым для IDE функционалом (зачем компилятору хранить в AST вайтспейс и комментарии? зачем компилятору уметь API для модификаций AST и их трансляции обратно в текст? Зачем компилятору уметь быстро искать использования членов типов в других файлах? Зачем компилятору уметь переименовывать эти использования?).
2)… написаны на каком-нибудь языке, на котором _НЕ НАПИСАН_ UI и ядро IDE. Это вообще никого не парит, я смотрю, будто от компилятора так мало информации надо и такой тривиальный протокол общения, что ничего не стоит сделать интероп. Ага ага
3)… во время своего развития просто удаляют какие-то ставшими obsolete ошибками или предупредлениями, а вот от IDE хотят работу с разными версиями языка и нам приходится мейнтейнить все предыдущие версии языка
4)… не всегда существует! Как писать поддержку JavaScript тогда, о какие «стандартные сервисы»?

Никто почему-то не видит плюсов того, что все поддержки языков в IDE написаны на одном языке с использованием очень похожих концепций, что IDE не зависят от breaking changes в API и релизных циклов компиляторов, что некоторые вещи можно сделать лучше чем они сделаны в компиляторах и так далее.

Я могу дальше продолжать, но пользователям все равно виднее, эх :)
А вы научите, как вот так взять успешный коммерческий продукт, выбросить устоявшиеся и проверенные временем семантические и сентаксические модели, забить на опыт, забить на то что ReSharper — это вообще-то проект на managed-коде… и делать поддержку TypeScript на TypeScript, используя TypeScript-компилятор.

Реимплементить часть компилятора — это парадигма всех IDE JetBrains (за исключением, наверное, только поддержки Kotlin). Это сложный выбор, который был сделан давно и показал себя с хорошей стороны. Да, мы часто страдаем от расхождений компиляторов и IDE, да нам нужны сильные программисты чтобы содержать много сложного компиляторо-подобного кода, но есть и множество плюсов: мы не зависим от релизных циклов компиляторов, мы можем писать поддержки любых языков на одном managed-языке (а не на языке компилятора) и шарить общую платформу и разные высокоуровневые модели, мы можем успешно поддерживать не последние версии языка (в компиляторах обычно нельзя включить диалект предыдущей версии или «отключить» breaking changes новой версии компилятора). Я буквально могу взять мелкую фичу из C#, удалить работу с типами и C#-специфичными выражениями и у меня получится практически рабочая фича для JavaScript. То есть я могу писать JS-поддержку практически в тех же терминах, что и в C# — и это просто замечательно, еще и применимо ко всем языкам.

p.s. Бага в TypeScript не воспроизвелась у меня в 9.1, а багу в C# я для вас починю обязательно.
Было бы просто замечательно, если бы вы смогли снять снепшотик managed-хипа разожравшегося devenv.exe триалочкой dotMemory, чтобы у нас было представление что там распухло в памяти так :(
Нет, про сам ReSharper.

ReSharper — это и есть «Roslyn», который случился на почти 10 лет раньше, изначально на managed коде, изначально с поддержкой нескольких языков (включая не CLR-языки). Единственные концептуальные отличия — у нас просто нет бэкендов (хотя можно было бы сделать) + в отличие от традиционных компиляторов мы заточены под постоянные изменения текста/AST (инкрементальность во все поля) и работу с незаконченным кодом. Все что сейчас толкает Roslyn — синтаксические деревья, семантические модели, движки dataflow-анализов, документные и проектные модели — были у нас реализованы когда Roslyn'а и в планах не было.

Но люди почему-то все равно уверены, что 10 лет технических инвестиций в это все надо просто выкинуть, а тонны кода анализов и фиксов C#/VB переписать на другое API, несовместимое с нашим привычным и используемым во всех других языках. Эх :)
У нового «стандартного» меню (который еще не зарелизен и есть только в VS2015) нет и половины функционала нашей менюшки, нет встроенного поиска, другой стандартный хоткей, другая модель сортировки и группировки, его API до сих пор переделывали в последних CTP, студийная лампочка показывается в другом margin'е редактора (в который нам нетривиально добросить свои другие индикаторы, типа юнит-тестов, рекурсии, имплементации интерфейсов) и так далее.

Интересная у вас логика!
Я так понимаю, что второй абзац не про «Evaluate expression» (он поддерживает исполнение кода только из BCL), а про import type popup. Дело в том, что типы из сборки System.Drawing.dll находятся решарпером не потому, что мы его куда-то захардкодили сборку или что-то подобное, а потому что оно попало к нам в кэш бинарных сборок и попробовать поискать в ней тип для нас — очень простая операция. Другое дело — как сборки попадают в этот кэш. Обычно туда попадает какой-то миниальный сабсет сборок BCL + все бинарные сборки всех референсов всех проектов солюшена. Если сборку System.Net.Http.dll референсит какой-нибудь соседний проект в солюшене, то import popup должен его вам предложить (иначе это баг).

Загружать в этот кэш все, что предлагает Visual Studio в диалоге «Add reference...» — не разумно, поэтому импорт типов и не работает по всем сборкам машины. У нас есть идеи как можно сделать такой импорт типов из всех сборок SDK текущей машины, возможно это будет продолжением новой фичи «Find type on nuget.org» (то есть поиск будет только по требованию, а не импорт-попапом). На данный момент, к сожалению, простого решения этой проблемы в ReSharper не существует.
Пошерстил тесты, оказалось что разработчик фичи просто не протестировал случаи с несколькими вызовами индексаторов. То есть есть тесты с какими угодно инициализаторами, несколькими параметрами, перегрузками и т.п., но всегда с одним вызовом:

class D {
  public int this[int i, int j] {
    get { return 1; }
    set { }
  }
}

class C {
  public static void Foo() {
    D d = new D(){};
    d[1,2] {caret}= 5;
  }
}


Мы когда-то заимплементили эти инициализаторы вместе с event initializers, а потом последние пришлось выпиливать, с порядком вычисления аргументов инициализаторов было не ясно (давно делали это) — где-то по пути забыли обработать случаи нескольких вызовов… :(
Я говорил про программную верификацию корректности рефакторингов и других фич («IDE-тулинга»), каким-нибудь proof assistant'ом :)

Конечно все тестируется обычными модульными и интеграционными тестами (>45 000 тестов на каждый билд) + интеграционными тестами тестами исполняющимися в Visual Studio на куче разных виртуалок в разных конфигурациях.
Это особенности багтрекинга в ReSharper :)

Конечный приоритет все равно выберет разработчик, а мне проще починить этот баг, чем выбирать ему приоритет чтобы всех устроил. Поменял на show-stopper, вам так лучше?

Насчет сомнений в фиче — я с вами согласен, ошибки в тулинге достаточно опасны и последствия могут быть печальными. Что еще тут сказать, верифицировать корректность IDE-тулинга еще не научились, значит будем просто чинить :)
Мы не снижали «планку качества». Поддержка тех или иных конструкций C# 6.0, которые не подверглись изменениями в последнее время и дизайн которых был достаточно устоявшимся, сделана достойного качества. Остальное — особенности эволюции C# 6.0 и Roslyn, на которые мы не можем повлиять.

Не стоит думать, что релиз делается только из-за одной поддержки C# 6.0 и мы сколько угодно можем подгонять release schedule под VS2015, в ReSharper 9.0 множество других возможностей которые мы считаем осмысленным зарелизить сейчас. Сделать поддержку C# 6.0 плагином — нереально, изменения приходится делать по коду всей C#-поддержки, от самого ядра (а от него ависит очень много кода и фич, все приходится ревьювить в условиях нового языка).

Про неподдержанные фичи напишем, спасибо.
Маленький баг в «Use object initializer» вас смутил, а то что сама фича («Dictionary initializers») до сих пор не сделана нормально и семантика не ясна — это наверное нормально…

Например, сейчас сайд-эффект вычисления выражения под инициализатором дублируется для каждого nested-инициализатора. Такого нет больше ни в одном месте языка. Пофиксить обещали полгода назад :)
Если вы серъезно считаете, что для новых фич языка программирования сложности C# можно сделать что-то типа «Add to user dictionary», то очень глубоко заблуждаетесь.

Новые языковые конструкции надо как минимум попарсить, парсер не может парсить где угодно разный неизвестный «мусор», а потом просто игнорировать его при построении синтаксических деревьев. ReSharper — это не только синтаксический тул, а куда больше семантический. Нельзя просто «добавить» парсинг «a?.B(x)» потому что "?." влияет на control flow граф (а он на dataflow анализы), добавляет множество новых ошибок компиляции, влияет на тип выражения («поднимает» типы-значения до nullable-типов) и так далее (это вообще другая нода AST, не то же самое что «a.B(x)»). Оператор nameof имеет особые правила того, что можно под ним упоминать (которые меняются до сих пор!), а отличить оператор nameof от вызова метода nameof() можно только семантически (так же как с классом «var»).

Я понимаю, что для конечного пользователя выглядит что после релиза R# парни из MS просто поменяли «using System.Console;» на «using static System.Console;», а решарпер этого не поддерживает. А для разработчика тула — это две недели работы, потому что если разобраться, то using static начал открывать не только статические классы, но и любые другие типы + открывать extension-методы. А значит надо отревьювить весь код от вставки using'ов, оптимизации using'ов, форматтера (он умеет using'и на группы разбивать), фич вставки и инлайна using static'ов, ошибок компиляции, инспекций типа redundant qualifier, запретить использование using static в razor/aspx-разметках, прикрутить using static к фиче «Import on paste», использовать возможность импортировать extension-методы из конкретных типов для разрешения конфликтов (открытие целого namespace'а может создавать конфликты с другими extension-методами, теперь можно будет пробовать решать это открывая лишь один using static System.Linq.Enumerable, например). И это только вершина айсберга, я не упоминаю всякие кэши extension-методов и сколько кода надо переписать чтобы добавить импорт extension-методов из конкретных типов :)

К сожалению, R# всегда имеет некоторый разрыв от эталонной реализации языка и мы всегда работаем чтобы свести эту разницу к нулю. В IDE-тулинге невозможно сделать такой механизм, позволяющий просто игнорировать новые конструкции с неизвестной семантикой, по крайней мере для статически типизированных языков.
Это еще не известно (в таких условиях работаем, ага), но скорее всего именно такой синтаксис попадет в релиз. Вероятно, что вместе с verbatim-интерполяцией:

var path = $@"{programFiles}\MyApp"
Мы писали об этом, например фрагмент из блогпоста о RTM 9.0:

«Support for C# 6.0. Although the final C# 6.0 language design is not yet clear enough, ReSharper 9.0 already provides support for most of what is defined. New code suggestions help migrate your existing code to more concise C# 6.0...»

Со страницы «What's new in ReSharper»:

«Initial support for C# 6.0»
«Although the feature set for C# 6.0 is not yet finalized, ReSharper 9 already provides support for such new language constructs...»

Возможно, это действительно недостаточно очевидно, что продукт выпускающийся намного раньше официального релиза VS2015 физически не может поддерживать релизную версию языка, потому что ее просто еще не существует. К сожалению, релизить продукт — это сложный процесс, требующих коммунникации между многими людьми, возможно мы действительно потеряли часть информации при подготовки релизных материалов. Мы уточним информацию о поддерживаемом сабсете C# 6.0 на сайте и в блогпостах.
Один из разработчиков Microsoft Roslyn (@KirillOsenkov) недавно рассказывал, что они используют exception filters внутри тестов для одной интересной задачи — автоматического снятие минидампа процесса до непосредственно разворачивания стека при возникновении исключения.

Хотел написать такой же хелпер (если DynamicMethod позволяет делать BeginExceptFilterBlock, конечно, в чем я сомневаюсь), что-нибудь типа MiniDump.Execute(() => { ... }) в топ левеле и чтобы процесс сам себя дампил, да дампами не пользуюсь вообще (процессы слишком жирные).

Еще вроде в статье не упомянуто, что в случае возникновения исключения в filter-блоке, это равнозначно значению 0 на стеке по окончанию исполнения фильтра (то есть предикат фильтрации считается вернувшим false), а само исключение безвозвратно теряется.

В dotPeek не сделано ввиду исключительной редкости данной конструкции, постраюсь запилить на следующую версию ;)
1. Касаемо «пофиксили» — поля стали создаваться рядом с другими полями не из-за какого-либо бага, а из-за пачки реквестов пользователей, которым надоело самим таскать поля после контекстных действий. Исследовав ситуацию с предпочтениями расположения полей в layout'е классов в различных доступных солюшенах пришли к выводу, что расположение поля рядом со свойством — куда менее распространенный кейс, чем группировка полей вместе.

2. Как только возникают подобные ситуации (http://xkcd.com/1172/) — помогает завести нам реквест или проголосовать за существующий. Где-то я такой реквест видел, наверное прийдётся сделать настроечку на 8.1, чтобы были счастливы все. Простите, если это доставило так много неудобств.

3. От меня лично: от схемы layout'а классов «поля рядом с их свойствами» на самом деле лучше отказаться, так как никто не может толком ответить — где располагать поля, не привязанные к свойствам? Большинство располагают в начале класса, но почему тогда внезапно имеет смысл размещать другие поля рядом со свойствами — не понятно. Совсем не понятно, где размещать поле, если оно может рассматриваться как «backing field» сразу нескольких свойств. Само понятие — «backing field» — очень сложно даже лишь пытаться формализовать (public List XS { return xs ?? (xs = new List()); } — тут «xs», это «backing field»? А если в акцессорах встречаются несколько полей? И ещё очень много чего магического может быть написано в акцессорах). В решарпере минимум эвристик на эту тему. Подобный class layout невозможно выразить в нашем code cleanup для автоматического реордеринга членов классов, не смотря на сложность языка описания class layout'а. Ну и из личного опыта — когда весь стейт типа в одном месте, это удобнее, чем внезапно обнаруживать поля где-нибудь между методов в совсем неожиданных местах.
А в плагине Agent Mulder для поддержки IoC-контейнеров в R# есть поддержка плагинов через MEF — то есть это плагины для плагина для плагина для VS :)

Информация

В рейтинге
Не участвует
Откуда
Москва, Москва и Московская обл., Россия
Дата рождения
Зарегистрирован
Активность