Анализаторы Roslyn и библиотека с поддержкой кода для неизменяемыхArrays
Платформа компилятора .NET ("Roslyn") помогает создавать библиотеки, учитывающие код. Библиотека с поддержкой анализа кода предоставляет функциональные возможности и инструменты (включая анализаторы Roslyn), которые помогут эффективно использовать библиотеку или избежать ошибок. В этом разделе показано, как создать практический анализатор Roslyn для выявления распространенных ошибок при использовании пакета NuGet System.Collections.Immutable. В примере также показано, как предоставить исправление кода для проблемы с кодом, обнаруженной анализатором. Пользователи видят исправления кода в интерфейсе лампочки в Visual Studio и могут автоматически применять исправление кода.
Начало работы
Для создания этого примера вам потребуется следующее:
- Visual Studio 2015 (а не Express Edition) или более поздней версии. Вы можете использовать бесплатный Visual Studio Community Edition
- пакет SDK Visual Studio. Кроме того, при установке Visual Studio выберите Инструменты расширяемости Visual Studio в категории Common Tools, чтобы установить пакет SDK одновременно. Если вы уже установили Visual Studio, вы также можете установить этот пакет SDK, перейдя в главное меню Файл>Создать>Project, выбрав C# в области навигации слева, а затем выберите расширяемость. При выборе шаблона проекта "Установить средства расширяемости Visual Studio" вам будет предложено скачать и установить SDK.
- SDK для платформы компилятора .NET ("Roslyn"). Вы также можете установить этот SDK, перейдя в главное меню Файл>Создать>Project, выбрав C# в левой панели навигации и выбрав Расширяемость. При выборе шаблона проекта "Скачать SDK для платформы компилятора .NET" появится предложение скачать и установить набор средств SDK. Этот пакет SDK включает визуализатор синтаксиса Roslyn. Это полезное средство помогает определить, какие типы моделей кода следует искать в анализаторе. Инфраструктура анализатора вызывает код для определенных типов модели кода, поэтому код выполняется только при необходимости и может сосредоточиться только на анализе соответствующего кода.
В чём проблема?
Представьте, что вы предоставляете библиотеку с поддержкой ImmutableArray (например, System.Collections.Immutable.ImmutableArray<T>) . Разработчики C# имеют широкий опыт работы с массивами .NET. Однако из-за характера неизменяемых массивов и методов оптимизации, используемых в реализации, интуитивные представления разработчиков на C# приводят к тому, что пользователи вашей библиотеки пишут ошибочный код, как описано ниже. Кроме того, пользователи не видят свои ошибки до момента выполнения программы, что не соответствует качеству, к которому они привыкли в Visual Studio с .NET.
Пользователи знакомы с написанием кода следующим образом:
var a1 = new int[0];
Console.WriteLine("a1.Length = {0}", a1.Length);
var a2 = new int[] { 1, 2, 3, 4, 5 };
Console.WriteLine("a2.Length = {0}", a2.Length);
Создание пустых массивов для заполнения последующих строк кода и использования синтаксиса инициализатора коллекции знакомы разработчикам C#. Тем не менее, написание того же кода для неизменяемого массива вызывает сбой во время выполнения.
var b1 = new ImmutableArray<int>();
Console.WriteLine("b1.Length = {0}", b1.Length);
var b2 = new ImmutableArray<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("b2.Length = {0}", b2.Length);
Первая ошибка обусловлена тем, что реализация ImmutableArray использует структуру для упаковки базового хранилища данных. Структуры должны иметь конструкторы без параметров, чтобы default(T)
выражения могли возвращать структуры со всеми нулевыми или пустыми элементами. Когда код обращается к b1.Length
, возникает ошибка разыменовки null во время выполнения, так как в структуре ImmutableArray нет базового массива хранилища. Правильный способ создания пустого неизменяемого массива — ImmutableArray<int>.Empty
.
Ошибка с инициализаторами коллекции происходит, так как метод ImmutableArray.Add
возвращает новые экземпляры при каждом вызове. Поскольку ImmutableArrays никогда не изменяются, после добавления нового элемента вы получите новый объект ImmutableArray (который может использовать общее хранилище для повышения производительности с ранее существующим ImmutableArray). Так как b2
указывает на первый неизменяемый массив перед тем, как пять раз вызвать Add()
, b2
является неизменяемым массивом по умолчанию. Вызов функции Length также приводит к ошибке разыменования null. Правильный способ инициализации неизменяемого массива без ручного вызова Add — использовать ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5})
.
Поиск соответствующих типов синтаксического узла для активации анализатора
Чтобы начать сборку анализатора, сначала выясните, какой тип узла синтаксиса необходимо искать. Запустите визуализатор синтаксиса из меню Вид>Другие окна>визуализатор синтаксиса Roslyn.
Поместите курсор редактора в строку, которая объявляет b1
. Вы увидите, что визуализатор синтаксиса показывает, что вы находитесь в узле LocalDeclarationStatement
дерева синтаксиса. Этот узел имеет VariableDeclaration
, который, в свою очередь, имеет VariableDeclarator
, который, в свою очередь, имеет EqualsValueClause
, и, наконец, есть ObjectCreationExpression
. При щелчке в дереве узлов визуализатора синтаксиса, синтаксис в окне редактора подсвечивается, чтобы показать код, представленный этим узлом. Имена подтипов SyntaxNode соответствуют именам, используемым в грамматике C#.
Создание проекта анализатора
В главном меню выберите Файл>Создать>Проект. В диалоговом окне нового проекта в разделе проектов C# на левой панели навигации выберите Расширяемость, а в правой области выберите шаблон проекта Анализатор с исправлением кода. Введите имя и подтвердите диалоговое окно.
Шаблон открывает файл DiagnosticAnalyzer.cs. Выберите вкладку буфера редактора. Этот файл имеет класс анализатора (сформированный из имени, которое вы предоставили проекту), который является производным от DiagnosticAnalyzer
(тип API Roslyn). Четкость DiagnosticAnalyzerAttribute
в вашем новом классе указывает, что ваш анализатор относится к языку C#, что позволяет компилятору обнаруживать и загружать его.
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ImmutableArrayAnalyzer : DiagnosticAnalyzer
{}
Анализатор можно реализовать с помощью Visual Basic, предназначенный для кода C#, и наоборот. Более важно в diagnosticAnalyzerAttribute выбрать, предназначен ли анализатор для одного языка или обоих. Более сложные анализаторы, требующие подробного моделирования языка, могут быть нацелены только на один язык. Если анализатор, например, проверяет только имена типов или имена общедоступных членов, возможно, можно использовать общую языковую модель Roslyn в Visual Basic и C#. Например, FxCop предупреждает, что класс реализует ISerializable, но класс не имеет атрибута SerializableAttribute независимо от языка и работает как для кода Visual Basic, так и для C#.
Инициализация анализатора
Прокрутите вниз немного в классе DiagnosticAnalyzer
, чтобы увидеть метод Initialize
. Компилятор вызывает этот метод при активации анализатора. Метод принимает объект AnalysisContext
, позволяющий анализатору получать сведения о контексте и регистрировать обратные вызовы для событий для типов кода, который требуется проанализировать.
public override void Initialize(AnalysisContext context) {}
Откройте новую строку в этом методе и введите "context", чтобы просмотреть список завершения IntelliSense. В списке завершения вы можете увидеть много методов Register...
для обработки различных видов событий. Например, первый, RegisterCodeBlockAction
, выполняет обратный вызов к вашему коду для блока, который обычно представляет собой код между фигурными скобками. Регистрация блока также вызывает код для инициализатора поля, значения атрибута или значения необязательного параметра.
В качестве другого примера, RegisterCompilationStartAction
вызывает код при начале компиляции, что полезно при сборе состояния в разных местах. Можно создать структуру данных для сбора всех используемых символов, и каждый раз, когда анализатор вновь вызывается для обработки какого-либо синтаксиса или символа, вы сможете сохранять информацию о каждом месте в этой структуре данных. Когда вас вызывают обратно из-за окончания компиляции, вы можете проанализировать все сохранённые расположения, чтобы, например, сообщить, какие символы код использует из каждой инструкции using
.
С помощью Визуализатора синтаксисавы поняли, что хотите, чтобы вас вызывали, когда компилятор обрабатывает выражение создания объекта ObjectCreationExpression. Этот код используется для настройки обратного вызова:
context.RegisterSyntaxNodeAction(c => AnalyzeObjectCreation(c),
SyntaxKind.ObjectCreationExpression);
Вы регистрируете узел синтаксиса и настраиваете фильтр только для узлов синтаксиса, связанных с созданием объектов. По общепринятой практике авторы анализаторов используют лямбда при регистрации действий, что помогает анализаторам оставаться независимыми от состояния. Для создания метода AnalyzeObjectCreation
можно использовать функцию Visual Studio создания использования. Это обеспечивает также правильный тип параметра контекста для вас.
Задайте свойства для пользователей вашего анализатора
Чтобы ваш анализатор отображался в пользовательском интерфейсе Visual Studio соответствующим образом, найдите и измените следующую строку кода, чтобы идентифицировать ваш анализатор.
internal const string Category = "Naming";
Измените "Naming"
на "API Guidance"
.
Затем найдите и откройте файл Resources.resx в вашем проекте с помощью обозревателя решений. Вы можете указать описание для вашего анализатора, заголовок и т. д. Сейчас вы можете изменить значение всех этих элементов на "Don't use ImmutableArray<T> constructor"
. Аргументы форматирования строк можно поместить в строку ({0}, {1}и т. д.), а затем при вызове Diagnostic.Create()
можно указать массив аргументов params
, передаваемый.
Анализ выражения создания объекта
Метод AnalyzeObjectCreation
принимает другой тип контекста, предоставленного платформой анализатора кода.
AnalysisContext
метода Initialize
позволяет регистрировать обратные вызовы действий для настройки анализатора. Например, SyntaxNodeAnalysisContext
имеет CancellationToken
, которые можно обойти. Если пользователь начинает вводить текст в редакторе, Roslyn отменяет выполнение анализаторов, чтобы уменьшить нагрузку и повысить производительность. В качестве другого примера этот контекст имеет свойство Node, которое возвращает узел синтаксиса создания объекта.
Получите узел, который можно предположить, является типом, для которого отфильтровано действие синтаксического узла:
var objectCreation = (ObjectCreationExpressionSyntax)context.Node;
Запустите Visual Studio с вашим анализатором в первый раз
Запустите Visual Studio, создав и выполнив анализатор (нажмите клавишу F5). Поскольку начальный проект в Solution Explorer является проектом VSIX, выполнение вашего кода приводит к построению кода и VSIX, а затем запускает Visual Studio с установленным VSIX. При запуске Visual Studio таким образом он запускается с отдельным кустом реестра, чтобы основное использование Visual Studio не влияло на экземпляры тестирования при создании анализаторов. При первом запуске Visual Studio выполняет несколько инициализаций, аналогичных при первом запуске Visual Studio после его установки.
Создайте консольный проект, а затем введите код массива в метод Main вашего консольного приложения.
var b1 = new ImmutableArray<int>();
Console.WriteLine("b1.Length = {0}", b1.Length);
var b2 = new ImmutableArray<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("b2.Length = {0}", b2.Length);
Строки кода с ImmutableArray
имеют волнистые линии, так как необходимо получить неизменяемый пакет NuGet и добавить оператор using
в код. Нажмите правую кнопку указателя на узле проекта в обозревателе решений и выберите Управление пакетами NuGet. В диспетчере NuGet введите "Неизменяемый" в поле поиска и выберите элемент System.Collections.Immutable (не выбирайте Microsoft.Bcl.Immutable) в левой области и нажмите кнопку "Установить" в правой области. Установка пакета добавляет ссылку в список ссылок проекта.
Вы по-прежнему видите красные волнистые линии под ImmutableArray
, поэтому поместите курсор в этот идентификатор и нажмите клавиши Ctrl +и (точка), чтобы открыть меню предложенных исправлений и выбрать пункт для добавления соответствующей директивы using
.
Сохраните всё и закройте второй экземпляр Visual Studio, чтобы временно привести систему в чистое состояние для продолжения.
Завершите анализатор, используя функцию "редактировать и продолжить"
В первом экземпляре Visual Studio задайте точку останова в начале метода AnalyzeObjectCreation
путем нажатия клавиши F9 с помощью курсора на первой строке.
Запустите анализатор снова с помощью F5, а во втором экземпляре Visual Studio снова откройте консольное приложение, созданное в последний раз.
Вы вернётесь к первому экземпляру Visual Studio в точке останова, так как сам компилятор Roslyn заметил выражение создания объекта и вызвал ваш анализатор.
Получите узел для создания объекта. Перейдите через строку, задающую переменную objectCreation
, нажав клавишу F10, и в окне немедленных результатов выполните выражение "objectCreation.ToString()"
. Вы видите, что синтаксический узел, на который указывает переменная, является кодом "new ImmutableArray<int>()"
, именно то, что вы ищете.
Получите объект ImmutableArray типа<T>. Необходимо проверить, является ли созданный тип неизменяемымArray. Сначала вы получите объект, представляющий этот тип. Вы проверяете типы с помощью семантической модели, чтобы убедиться, что у вас есть правильный тип, и вы не сравниваете строку из ToString()
. Введите следующую строку кода в конце функции:
var immutableArrayOfTType =
context.SemanticModel
.Compilation
.GetTypeByMetadataName("System.Collections.Immutable.ImmutableArray`1");
Вы обозначаете универсальные типы в метаданных с обратными апострофами (`) и количеством универсальных параметров. Именно поэтому вы не видите "...ImmutableArray<T>" в имени метаданных.
Семантическая модель содержит много полезных вещей, которые позволяют задавать вопросы о символах, потоке данных, времени существования переменной и т. д. Roslyn отделяет узлы синтаксиса от семантической модели по различным причинам проектирования (производительность, моделирование ошибочного кода и т. д.). Требуется, чтобы модель компиляции искала информацию, содержащуюся в ссылках для точного сравнения.
Вы можете перетащить желтый указатель выполнения в левой части окна редактора. Перетащите его до строки, которая задает переменную objectCreation
, и с помощью F10перейдите через новую строку кода. Если навести указатель мыши на переменную immutableArrayOfType
, мы обнаружили точный тип в семантической модели.
Получите тип выражения создания объекта. Тип используется несколькими способами в этой статье, но это означает, что если у вас есть новое выражение Foo, необходимо получить модель Foo. Необходимо получить тип выражения создания объекта, чтобы увидеть, является ли это тип ImmutableArray<T>. Используйте семантическую модель еще раз, чтобы получить сведения о символах для символа типа (ImmutableArray) в выражении создания объекта. Введите следующую строку кода в конце функции:
var symbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol as INamedTypeSymbol;
Поскольку анализатору необходимо обрабатывать неполный или неправильный код в буферах редактора (например, отсутствует оператор using
), поэтому нужно проверить, что symbolInfo
является null
. Чтобы завершить анализ, необходимо получить именованный тип (INamedTypeSymbol) из объекта сведений о символах.
Сравните типы. Так как существует открытый универсальный тип T, который мы ищем, и тип в коде является конкретным универсальным типом, вы запрашиваете сведения о символах для того, какой тип создан (открытый универсальный тип) и сравниваете этот результат с immutableArrayOfTType
. Введите следующее в конце метода:
if (symbolInfo != null &&
symbolInfo.ConstructedFrom.Equals(immutableArrayOfTType))
{}
Сообщите о диагностике. Сообщать о диагностических данных довольно просто. Вы используете правило, созданное для вас в шаблоне проекта, которое определяется до метода Initialize. Так как эта ситуация в коде является ошибкой, можно изменить строку, которая инициализировала Rule, чтобы заменить DiagnosticSeverity.Warning
(зеленая волнистая линия) на DiagnosticSeverity.Error
(красная волнистая линия). Остальная часть правила инициализируется из ресурсов, которые вы редактировали в начале пошагового руководства. Кроме того, необходимо сообщить о расположении для зигзагообразной линии, которое является местонахождением спецификации типа выражения создания объекта. Введите этот код в блоке if
:
context.ReportDiagnostic(Diagnostic.Create(Rule, objectCreation.Type.GetLocation()));
Функция должна выглядеть следующим образом (возможно, форматирована по-другому):
private void AnalyzeObjectCreation(SyntaxNodeAnalysisContext context)
{
var objectCreation = (ObjectCreationExpressionSyntax)context.Node;
var immutableArrayOfTType =
context.SemanticModel
.Compilation
.GetTypeByMetadataName(
"System.Collections.Immutable.ImmutableArray`1");
var symbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol as
INamedTypeSymbol;
if (symbolInfo != null &&
symbolInfo.ConstructedFrom.Equals(immutableArrayOfTType))
{
context.ReportDiagnostic(
Diagnostic.Create(Rule, objectCreation.Type.GetLocation()));
}
}
Удалите точку останова, чтобы вы могли видеть работу анализатора (и перестать возвращаться к исходному экземпляру Visual Studio). Перетащите указатель выполнения в начало метода и нажмите клавишу F5 F5, чтобы продолжить выполнение. Когда вы снова перейдете к второму экземпляру Visual Studio, компилятор снова начнет изучать код, и он вызовет анализатор. Под ImmutableType<int>
можно увидеть волнистую линию.
Добавление элемента "Исправление кода" для проблемы с кодом
Прежде чем начать, закройте второй экземпляр Visual Studio и остановите отладку в первом экземпляре Visual Studio (где разрабатывается анализатор).
Добавьте новый класс. Используйте контекстное меню (правую кнопку указателя) на узле проекта в обозревателе решений и выберите добавить новый элемент. Добавьте класс с именем BuildCodeFixProvider
. Этот класс должен быть производным от CodeFixProvider
, и вам потребуется использовать CTRL+. (период), чтобы вызвать исправление кода, которое добавляет правильную инструкцию using
. Этот класс также должен быть аннотирован с помощью атрибута ExportCodeFixProvider
, и необходимо добавить инструкцию using
для разрешения перечисления LanguageNames
. В нем должен быть файл класса со следующим кодом:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
namespace ImmutableArrayAnalyzer
{
[ExportCodeFixProvider(LanguageNames.CSharp)]
class BuildCodeFixProvider : CodeFixProvider
{}
Создайте заглушки для производных элементов. Теперь поместите курсор редактора в идентификатор CodeFixProvider
и нажмите клавиши CTRL ctrl+. (период), чтобы заглушить реализацию для этого абстрактного базового класса. Это создает свойство и метод для вас.
Реализуйте свойство. Заполните тело get
свойства FixableDiagnosticIds
следующим кодом:
return ImmutableArray.Create(ImmutableArrayAnalyzer.DiagnosticId);
Roslyn объединяет диагностику и исправления путем сопоставления этих идентификаторов, которые являются просто строками. Шаблон проекта создал идентификатор диагностики для вас, и вы можете изменить его. Код в свойстве просто возвращает идентификатор из класса анализатора.
Метод RegisterCodeFixAsync принимает контекст. Контекст важен, так как исправление кода может применяться к нескольким диагностическим проблемам или на одной строке кода может возникнуть более одной проблемы. Если ввести «context.» в текст метода, будет отображаться список полезных членов автозаполнения IntelliSense. Есть член CancellationToken, который можно проверить, чтобы выяснить, хочет ли какая-либо операция отменить выполнение. Существует элемент Document с большим количеством полезных элементов и позволяет получить доступ к объектам модели проекта и решения. Существует элемент Span, который является началом и концом расположения кода, указанного при сообщении о диагностике.
Сделайте метод асинхронным. Первое, что необходимо сделать, — исправить объявление созданного метода, чтобы он стал методом async
. Исправление кода для устранения реализации абстрактного класса не включает ключевое слово async
, даже если метод возвращает Task
.
Получите корень дерева синтаксиса. Чтобы изменить код, необходимо создать новое дерево синтаксиса с изменениями исправления кода. Для вызова GetSyntaxRootAsync
требуется Document
из контекста. Это асинхронный метод, так как работа по получению дерева синтаксиса может включать непредсказуемые шаги, такие как получение файла с диска, его анализ и построение модели кода Roslyn. Пользовательский интерфейс Visual Studio должен оставаться отзывчивым в это время, чему способствует использование async
. Замените строку кода в методе следующим образом:
var root = await context.Document
.GetSyntaxRootAsync(context.CancellationToken);
Найдите узел с проблемой. Вы передаете диапазон контекста, но узел, который вы нашли, может не быть кодом, который необходимо изменить. Диагностика сообщила только диапазон для идентификатора типа (где находилась волнистая линия), но необходимо заменить всё выражение создания объекта, включая ключевое слово new
в начале и скобки в конце. Добавьте следующий код в метод (и используйте клавиши CTRL +. для добавления инструкции using
для ObjectCreationExpressionSyntax
):
var objectCreation = root.FindNode(context.Span)
.FirstAncestorOrSelf<ObjectCreationExpressionSyntax>();
Зарегистрируйте исправление кода для пользовательского интерфейса лампочки. Когда вы регистрируете исправление кода, Roslyn автоматически подключается к интерфейсу подсказок Visual Studio. Конечные пользователи увидят, что могут использовать Ctrl+, (точка), когда ваш анализатор выделяет использование плохого конструктора ImmutableArray<T>
. Поскольку поставщик исправлений кода выполняется только при возникновении проблемы, вы можете быть уверены, что у вас есть нужное выражение создания объекта. В параметре контекста можно зарегистрировать новое исправление кода, добавив следующий код в конец метода RegisterCodeFixAsync
:
context.RegisterCodeFix(
CodeAction.Create("Use ImmutableArray<T>.Empty",
c => ChangeToImmutableArrayEmpty(objectCreation,
context.Document,
c)),
context.Diagnostics[0]);
Необходимо поместить курсор редактора в идентификатор, CodeAction
, а затем использовать CTRL+. (период), чтобы добавить соответствующую инструкцию using
для этого типа.
Затем поместите курсор редактора в идентификатор ChangeToImmutableArrayEmpty
и используйте CTRL+. еще раз, чтобы создать шаблон этого метода.
Этот последний фрагмент кода, который вы добавили, регистрирует исправление кода, передав CodeAction
и идентификатор диагностики для типа обнаруженной проблемы. В этом примере существует только один идентификатор диагностики, для который этот код предоставляет исправления, поэтому можно просто передать первый элемент массива идентификаторов диагностики. При создании CodeAction
вы передаете текст, который интерфейс лампы должен использовать в качестве описания исправления кода. Вы также передаете функцию, которая принимает токен отмены и возвращает новый документ. В новом документе есть новое дерево синтаксиса, включающее исправленный код, который вызывает ImmutableArray.Empty
. Этот фрагмент кода использует лямбда-выражение, чтобы замкнуть узел objectCreation и документ контекста.
Создайте новое дерево синтаксиса. В методе ChangeToImmutableArrayEmpty
, заглушки которого вы создали ранее, введите строку кода: ImmutableArray<int>.Empty;
. Если снова открыть окно средства "Syntax Visualizer" , вы увидите, что этот синтаксис является узлом SimpleMemberAccessExpression. Именно это этот метод должен создать и вернуть в новом документе.
Первое изменение ChangeToImmutableArrayEmpty
заключается в добавлении async
до Task<Document>
, так как генераторы кода не могут предположить, что метод должен быть асинхронным.
Заполните текст следующим кодом, чтобы метод выглядел следующим образом:
private async Task<Document> ChangeToImmutableArrayEmpty(
ObjectCreationExpressionSyntax objectCreation, Document document,
CancellationToken c)
{
var generator = SyntaxGenerator.GetGenerator(document);
var memberAccess =
generator.MemberAccessExpression(objectCreation.Type, "Empty");
var oldRoot = await document.GetSyntaxRootAsync(c);
var newRoot = oldRoot.ReplaceNode(objectCreation, memberAccess);
return document.WithSyntaxRoot(newRoot);
}
Вам потребуется поместить курсор редактора в идентификатор SyntaxGenerator
и использовать CTRL+. (период), чтобы добавить соответствующую инструкцию using
для этого типа.
Этот код использует SyntaxGenerator
, который является полезным типом для создания нового кода. После получения генератора для документа с проблемой в коде, ChangeToImmutableArrayEmpty
вызывает MemberAccessExpression
, выполняя передачу типа, у которого есть нужный нам член, и передавая имя этого члена в виде строки.
Затем метод извлекает корень документа, и так как это может включать произвольные операции в общем случае, код ожидает завершения этого вызова и передает токен отмены. Модели кода Roslyn неизменяемы, например работа со строкой .NET; При обновлении строки возвращается новый объект строки. При вызове ReplaceNode
возвращается новый корневой узел. Большая часть дерева синтаксиса является общим (так как она неизменяема), но узел objectCreation
заменяется на узел memberAccess
, а также все родительские узлы до корневого дерева синтаксиса.
Попробуйте свое исправление кода
Теперь можно, нажав F5, выполнить анализатор во втором экземпляре Visual Studio. Откройте проект консоли, который вы использовали раньше. Теперь вы должны увидеть, как лампочка появляется там, где создаётся ваше новое объектное выражение для ImmutableArray<int>
. Если нажать Ctrl +, а затем (период), вы увидите исправление кода и автоматически сгенерированный предварительный просмотр различий в коде в интерфейсе 'лампочка'. Рослин создает это для вас.
Полезный совет: Если вы запускаете второй экземпляр Visual Studio и не видите значок лампочки с предложенным исправлением кода, возможно, потребуется очистить кэш компонентов Visual Studio. Очистка кэша вынуждает Visual Studio повторно проверять компоненты, поэтому Visual Studio должна затем учитывать ваш последний компонент. Сначала закройте второй экземпляр Visual Studio. Затем в проводнике Windowsперейдите к %LOCALAPPDATA%\Microsoft\VisualStudio\16.0Roslyn\. (Число "16.0" меняется в зависимости от версии Visual Studio.) Удалите подкаталог ComponentModelCache.
Обсуждение видео и завершение проекта по кодированию
Вы можете просмотреть весь готовый код здесь. Вложенные папки DoNotUseImmutableArrayCollectionInitializer и DoNotUseImmutableArrayCtor каждая содержит файл C# для обнаружения проблем и файл C# для реализации исправлений кода, которые отображаются в графическом интерфейсе подсказок Visual Studio. Обратите внимание, что готовый код становится более абстрактным, чтобы не получать объект типа ImmutableArray<T> снова и снова. Он использует вложенные зарегистрированные действия для сохранения объекта типа в контексте, который всегда доступен при выполнении дочерних действий (анализ создания объектов и анализ инициализаций коллекции).