SlideShare a Scribd company logo
1 of 45
Roslyn API
SyntaxTree vs CodeDom
SemanticModel vs Reflection
Денис Цветцих
АстроСофт
www.astrosoft.ru
Почему это важно
О Roslyn много говорят
• Революция в мире компиляторов
• Позволит развивать C# быстрее
• OpenSource на GitHub
• Nuget packages Microsoft.CodeAnalysis.*
Что он даст для решения прикладных задач?
• Анализ кода
• Кодогенерация
2
Почему я здесь
Решал задачу кодогенерации
• Roslyn: Syntax tree
• CodeDom
Решал задачу анализа кода
• Roslyn: Semantic model
• Reflection
3
Cравнение Roslyn с
CodeDom и Reflection
Опрос
Кто решал задачи:
• кодогенерации с помощью CodeDom?
• анализа кода с помощью Reflection?
Кто сделал хотя бы один сэмпл использования Roslyn?
Кто применял Roslyn в своих проектах?
4
План на сегодня
• Возможности Roslyn для кодогенерации и анализа кода
• Какие из них и почему мы использовали
• Сравнение Roslyn с CodeDom и Reflection
5
Какая стояла задача
Клиент для ONVIF камеры на Windows Phone 8.1
• Работа с камерой через SOAP
• Но Windows Phone 8.1 не поддерживает SOAP сервисы
6
Решение: аналог “Add Service Reference”
• Собственный класс SoapClientBase
• По WSDL генерируем код утилитой SvcUtil
• Анализируем код при помощи Reflection
• Генерируем клиент SOAP сервиса при помощи CodeDom
• Анализ кода – SemanticModel
• Генерация кода – SyntaxTree
7
Абстрактное синтактическое дерево
Абстрактное синтактическое дерево (АСД,
англ. Abstract Syntax Tree, AST) – древовидное представление
синтаксической структуры программы
Узлы – конструкции языка программирования (классы,
методы, операторы)
Листья – неделимые синтаксические конструкции
(переменные, константы, ключевые слова)
8
Пример SyntaxTree
SyntaxNode: Синий
SyntaxToken: Зеленый
SyntaxTrivia: Красный
9
Console.WriteLine("Hello World");
Создание SyntaxTree
SyntaxTree syntaxTree =
CSharpSyntaxTree.ParseText("CODE");
Важно: SyntaxTree неизменяемо
10
Решение задачи №1
• Строим AST для кода, сгенерированного SvcUtil для .NET
• Перестраиваем с учетом специфики Windows Phone 8.1
• Сохраняем код перестроенного дерева
11
CSharpSyntaxRewriter
Реализация паттерна Visitor
SyntaxVisitor.Visit(SyntaxNode node)
SyntaxNode.Accept(SyntaxVisitor visitor)
Перегрузка методов SyntaxNode Visit*
SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
SyntaxNode VisitInterfaceDeclaration(InterfaceDeclarationSyntax node)
12
SyntaxRewriter: заменить тип на var
1 SyntaxNode VisitLocalDeclarationStatement
2 (LocalDeclarationStatementSyntax node)
3 {
4 var declarator = node.Declaration.Variables.First();
5 TypeSyntax variableTypeName = node.Declaration.Type;
6 TypeSyntax varTypeName = SyntaxFactory.IdentifierName("var");
7 return node.ReplaceNode(variableTypeName, varTypeName);
8 }
13
Применение SyntaxRewriter
+ Незначительные точечные изменения кода
- Значительные преобразования большого объема кода
14
Решение задачи №2
• Строим AST для кода, сгенерированного SvcUtil для .NET
• Анализируем AST. Получаем:
• интерфейсы, помеченные [ServiceContract]
• классы, помеченные [DataContract]
• На основе анализа строим AST для Windows Phone 8.1
• Сохраняем код построенного дерева
15
CSharpSyntaxWalker
Тот же самый Visitor
Перегрузка методов void Visit*
void VisitClassDeclaration(ClassDeclarationSyntax node)
void VisitInterfaceDeclaration(InterfaceDeclarationSyntax node)
16
Использование SyntaxWalker
1 private readonly SemanticModel _semanticModel;
2 public readonly List<INamedTypeSymbol> Services;
3
4 override void VisitInterfaceDeclaration(InterfaceDeclarationSyntax node)
5 {
6 var symbol = _semanticModel.GetDeclaredSymbol(node);
7 if (symbol.GetAttributes()
8 .Any(attr => attr.AttributeClass.Name == "ServiceContractAttribute"))
9 Services.Add(symbol);
10 }
17
API для кодогенерации
• Наследники SyntaxNode имеют internal конструктор
• Создание экземпляров при помощи SyntaxFactory
• Редактирование при помощи Fluent API методов With*
var decl = SyntaxFactory.ClassDeclaration("MyClass")
.WithBaseList(baseList)
.WithModifiers(modifiers)
.WithAttributeLists(attributes);
18
Создание namespace
Как реализовано
NameSyntax nameSyntax =
SyntaxFactory.ParseName("MyNamespace");
NamespaceDeclarationSyntax ns =
SyntaxFactory.NamespaceDeclaration(nameSyntax);
Как хочется
NamespaceDeclarationSyntax ns =
SyntaxFactory.NamespaceDeclaration("MyNamespace");
19
Добавление директивы using
Как реализовано
NamespaceDeclarationSyntax ns = …
var name = SyntaxFactory.ParseName("System");
var usingSyntax = SyntaxFactory.UsingDirective(name);
ns = ns.AddUsings(usingSyntax);
Как хочется
ns = ns.AddUsings("System");
20
Создание поля класса
Как реализовано
TypeSyntax type = ...
var variable = SyntaxFactory.VariableDeclarator("x");
var variableDecl = SyntaxFactory.VariableDeclaration(type);
var variableList = SyntaxFactory.SeparatedList(new[] {variable});
variableDecl = variableDecl.WithVariables(variableList);
var field = SyntaxFactory.FieldDeclaration(variableDecl);
// private int x, y, z;
// private int x;
Как хочется
var field = SyntaxFactory.FieldDeclaration("x", type);
21
Можно добавить много, нельзя один
• Переменная
• Модификатор класса, метода, свойства, …
• Реализуемые интерфейсы
22
SyntaxTree.Parse*
Преобразование строки в SyntaxNode
var body = string.Format(
"return this.CallAsync<{0}, {1}>("{2}", {3}.{4});",
v1, v2, v3, v4, v5);
SyntaxNode stmt = SyntaxFactory.ParseStatement(body);
23
Workspace
• MSBuildWorkspace – рабочая область
• Solution – набор проектов
• Project – проект, содержащий файлы исходного кода
• Document – файл исходного кода, содержит SyntaxTree и
соответствующую SemanticModel
24
Форматирование
var root = … // корень AST
MSBuildWorkspace workspace = MSBuildWorkspace.Create();
var formattedRoot = Formatter.Format(root, workspace);
File.WriteAllText("code.cs", formattedRoot.ToFullString());
25
Syntax API для кодогенерации
• Предназначено для решения общих задач
• Добавить классу список модификаторов
• API для решения частных задач отсутствует
• Нельзя добавить классу один модификатор
• Многословно
• Создать название, а потом уже namespace
• Решение – extension методы
26
Extension метод: добавить модификатор(ы)
1 public static PropertyDeclarationSyntax WithModifiers
2 (this PropertyDeclarationSyntax propertySyntax,
3 params SyntaxKind[] modifiers)
4 {
5 var tokenList = new SyntaxTokenList();
6 foreach (var modifier in modifiers)
7 {
8 tokenList = tokenList.Add(SyntaxFactory.Token(modifier));
9 }
10 return propertySyntax.WithModifiers(tokenList);
11 } 27
SyntaxTree vs CodeDom
SyntaxTree
± Нет API для решения частных
задач (extension method)
± Сразу генерируется код на C#
+ Наличие методов Parse*
CodeDom
+ Есть API для решения частных
задач
+ Строится модель, по ней
можно сгенерить C# и VB
28
Анализ кода
Compilation – аналог проекта. Содержит:
• Список элементов для компиляции
• Assembly references
29
Создание CSharpCompilation
SyntaxTree syntaxTree =
CSharpSyntaxTree.ParseText(File.ReadAllText(csFilePath));
var mscorlib =
MetadataReference.CreateFromAssembly(typeof(object).Assembly);
var compilation = CSharpCompilation.Create("ServiceReference",
new[] { syntaxTree },
new[] { mscorlib });
var semanticModel = compilation.GetSemanticModel(syntaxTree);
30
SemanticModel
Extension метод GetDeclaredSymbol:
по узлу SyntaxTree получить соответствующий семантический объект
IPropertySymbol GetDeclaredSymbol(PropertyDeclarationSyntax syntax)
INamedTypeSymbol GetDeclaredSymbol(BaseTypeDeclarationSyntax syntax)
31
Получение свойств класса
Как реализовано
ITypeSymbol typeSymbol = …
var properties = typeSymbol
.GetMembers()
.Where(m => m.Kind == SymbolKind.Property)
.Cast<IPropertySymbol>();
Как хочется
var properties = typeSymbol.GetProperties();
32
Является ли тип примитивом
Стандартной реализации нет. Можно реализовать так:
ITypeSymbol type;
var primitives = new HashSet<string>
{
"byte", "int", ext.
};
bool isPrimitive = primitives.Contains(type.ToString());
33
Анализ атрибутов
[XmlElement("elementName", typeof(Test))]
[XmlElement("elementName", Type = typeof(Test))]
public XmlElementAttribute(){}
public XmlElementAttribute(string elementName){}
public XmlElementAttribute(Type type){}
public XmlElementAttribute(string elementName, Type type){}
34
Reflection: параметр Type
var type = propertyInfo
.GetCustomAttribute<XmlElementAttribute>()
.Type;
И все! 
35
SemanticModel: AttributeData
ImmutableArray<TypedConstant>
ConstructorArguments { get; }
ImmutableArray<KeyValuePair<string, TypedConstant>>
NamedArguments { get; }
36
Ищем Type в NamedArguments
1 AttributeData attribute = … // анализируемый атрибут
2 TypedConstant? dataType; // результат
3
4 var named = attr.NamedArguments
5 .FirstOrDefault(item => item.Key == "Type");
6 if (!named.Equals(default(KeyValuePair<string, TypedConstant>)))
7 dataType = named.Value;
8 else // будем искать в параметрах конструктора
9 Продолжение на следующем слайде
37
Ищем Type в ConstructorArguments
9 if (attribute.ConstructorArguments.Length == 2)
10 dataType = attribute.ConstructorArguments.Last();
11 else
12 {
13 var arg = attribute.ConstructorArguments.FirstOrDefault();
14 if (!arg.Equals(default(TypedConstant)) &&
15 arg.Type.Name == "Type") //"Type" – System.Type
16 dataType = arg;
17 }
38
Собираем все вместе
1 AttributeData attribute = … // анализируемый атрибут
2 TypedConstant? dataType; // результат
3
4 var named = attr.NamedArguments.FirstOrDefault(item => item.Key == "Type");
5 if (!named.Equals(default(KeyValuePair<string, TypedConstant>))) dataType = named.Value;
6 else
7 if (attribute.ConstructorArguments.Length == 2)
8 dataType = attribute.ConstructorArguments.Last();
9 else {
11 var arg = attribute.ConstructorArguments.FirstOrDefault();
12 if (!arg.Equals(default(TypedConstant)) && arg.Type.Name == "Type")
13 dataType = arg;
14 }
39
SemanticModel для анализа кода
• Предназначена для решения общих задач
• Получить список членов класса
• Получить список элементов namespace
• API для решения частных задач отсутствует
• Нельзя получить отдельный список свойств класса
• Нельзя получить список классов namespace
• Нет проверки примитивности типа
• Сложная работа с атрибутами
40
Extension метод: получить свойства класса
1 public static IEnumerable<IPropertySymbol>
2 GetProperties(this ITypeSymbol typeSymbol)
3 {
4 return typeSymbol
5 .GetMembers()
6 .Where(m => m.Kind == SymbolKind.Property)
7 .Cast<IPropertySymbol>();
8 }
41
SemanticModel vs Reflection
SemanticModel
± Нет API для решения частных
задач (extension method)
- Нет проверки примитивности
типа
- Сложное получение
параметров атрибутов
Reflection
+ Есть API для решения частных
задач
+ Есть проверка примитивности
типа
+ Простое получение
параметров атрибутов
42
Впечатление от Roslyn API
Мне хотелось бы видеть Roslyn API таким, что:
• Позволяет быстро решать простые задачи
• Допускает решение сложных задач
43
Заключение
SyntaxTree
• SyntaxRewriter – для точечных изменений
• Многословность (создать имя, а потом сам namespace)
• Решает общие задачи (нельзя добавить один модификатор)
• Есть удобные методы Parse*
• Форматирование в пакете Workspace
SemanticModel
• Решает общие задачи (нельзя получить свойства класса)
• Нет проверки типа на примитивность
• Неудобная работа с атрибутами
44
Спасибо за внимание
Денис Цветцих
den.tsvettsih@yandex.ru

More Related Content

What's hot

Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10Dima Dzuba
 
Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8. Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8. Dima Dzuba
 
ук 03.001.02 2011
ук 03.001.02 2011ук 03.001.02 2011
ук 03.001.02 2011etyumentcev
 
Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6Dima Dzuba
 
C# Desktop. Занятие 02.
C# Desktop. Занятие 02.C# Desktop. Занятие 02.
C# Desktop. Занятие 02.Igor Shkulipa
 
C++ осень 2012 лекция 9
C++ осень 2012 лекция 9C++ осень 2012 лекция 9
C++ осень 2012 лекция 9Technopark
 
C++ Базовый. Занятие 04.
C++ Базовый. Занятие 04.C++ Базовый. Занятие 04.
C++ Базовый. Занятие 04.Igor Shkulipa
 
C# Desktop. Занятие 01.
C# Desktop. Занятие 01.C# Desktop. Занятие 01.
C# Desktop. Занятие 01.Igor Shkulipa
 
Михаил Давыдов - JavaScript. Базовые знания
Михаил Давыдов - JavaScript. Базовые знанияМихаил Давыдов - JavaScript. Базовые знания
Михаил Давыдов - JavaScript. Базовые знанияYandex
 

What's hot (10)

JRebel
JRebelJRebel
JRebel
 
Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10
 
Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8. Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8.
 
ук 03.001.02 2011
ук 03.001.02 2011ук 03.001.02 2011
ук 03.001.02 2011
 
Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6
 
C# Desktop. Занятие 02.
C# Desktop. Занятие 02.C# Desktop. Занятие 02.
C# Desktop. Занятие 02.
 
C++ осень 2012 лекция 9
C++ осень 2012 лекция 9C++ осень 2012 лекция 9
C++ осень 2012 лекция 9
 
C++ Базовый. Занятие 04.
C++ Базовый. Занятие 04.C++ Базовый. Занятие 04.
C++ Базовый. Занятие 04.
 
C# Desktop. Занятие 01.
C# Desktop. Занятие 01.C# Desktop. Занятие 01.
C# Desktop. Занятие 01.
 
Михаил Давыдов - JavaScript. Базовые знания
Михаил Давыдов - JavaScript. Базовые знанияМихаил Давыдов - JavaScript. Базовые знания
Михаил Давыдов - JavaScript. Базовые знания
 

Viewers also liked

Разработка Windows 8 приложений глазами WPF/Silverlight программиста
Разработка Windows 8 приложений глазами WPF/Silverlight программистаРазработка Windows 8 приложений глазами WPF/Silverlight программиста
Разработка Windows 8 приложений глазами WPF/Silverlight программистаDenis Tsvettsih
 
Модульная структура
Модульная структураМодульная структура
Модульная структураDenis Tsvettsih
 
Как приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложенияхКак приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложенияхDenis Tsvettsih
 
Эффективные email коммуникации
Эффективные email коммуникацииЭффективные email коммуникации
Эффективные email коммуникацииDenis Tsvettsih
 
Как написать XAML-приложение без Message Bus
Как написать XAML-приложение без Message Bus Как написать XAML-приложение без Message Bus
Как написать XAML-приложение без Message Bus Denis Tsvettsih
 
Как приручить реактивное программирование
Как приручить реактивное программированиеКак приручить реактивное программирование
Как приручить реактивное программированиеDenis Tsvettsih
 
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...Denis Tsvettsih
 
Разработка WPF приложений в стиле ViewModel First
Разработка WPF приложений в стиле ViewModel FirstРазработка WPF приложений в стиле ViewModel First
Разработка WPF приложений в стиле ViewModel FirstDenis Tsvettsih
 

Viewers also liked (9)

Project Roslyn: Exposing the C# and VB compiler’s code analysis
Project Roslyn: Exposing the C# and VB compiler’s code analysisProject Roslyn: Exposing the C# and VB compiler’s code analysis
Project Roslyn: Exposing the C# and VB compiler’s code analysis
 
Разработка Windows 8 приложений глазами WPF/Silverlight программиста
Разработка Windows 8 приложений глазами WPF/Silverlight программистаРазработка Windows 8 приложений глазами WPF/Silverlight программиста
Разработка Windows 8 приложений глазами WPF/Silverlight программиста
 
Модульная структура
Модульная структураМодульная структура
Модульная структура
 
Как приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложенияхКак приручить реактивное программирование в XAML приложениях
Как приручить реактивное программирование в XAML приложениях
 
Эффективные email коммуникации
Эффективные email коммуникацииЭффективные email коммуникации
Эффективные email коммуникации
 
Как написать XAML-приложение без Message Bus
Как написать XAML-приложение без Message Bus Как написать XAML-приложение без Message Bus
Как написать XAML-приложение без Message Bus
 
Как приручить реактивное программирование
Как приручить реактивное программированиеКак приручить реактивное программирование
Как приручить реактивное программирование
 
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
 
Разработка WPF приложений в стиле ViewModel First
Разработка WPF приложений в стиле ViewModel FirstРазработка WPF приложений в стиле ViewModel First
Разработка WPF приложений в стиле ViewModel First
 

Similar to Roslyn API : SyntaxTree vs CodeDom, SemanticModel vs Reflection

Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NETОпыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NETGoSharp
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Sergey Platonov
 
Универсальный сигнатурный анализ кода на C#, Java, PHP
Универсальный сигнатурный анализ кода на C#, Java, PHPУниверсальный сигнатурный анализ кода на C#, Java, PHP
Универсальный сигнатурный анализ кода на C#, Java, PHPИван Кочуркин
 
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворковНикита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворковSergey Platonov
 
kranonit S15 Vladimir Melnik - Ruby on Rails, BDD
kranonit S15 Vladimir Melnik - Ruby on Rails, BDDkranonit S15 Vladimir Melnik - Ruby on Rails, BDD
kranonit S15 Vladimir Melnik - Ruby on Rails, BDDKrivoy Rog IT Community
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioAndrey Karpov
 
Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...
Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...
Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...Yandex
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыcorehard_by
 
Как это работает: DLR
Как это работает: DLRКак это работает: DLR
Как это работает: DLRMikhail Shcherbakov
 
Convert this: peculiarities of cross-platform mobile game development at Vizor
Convert this: peculiarities of cross-platform mobile game development at VizorConvert this: peculiarities of cross-platform mobile game development at Vizor
Convert this: peculiarities of cross-platform mobile game development at VizorDevGAMM Conference
 
Ciklum .NET Saturday - Introduction to TypeScript
Ciklum .NET Saturday - Introduction to TypeScriptCiklum .NET Saturday - Introduction to TypeScript
Ciklum .NET Saturday - Introduction to TypeScriptDmytro Mindra
 
разработка бизнес приложений (8)
разработка бизнес приложений (8)разработка бизнес приложений (8)
разработка бизнес приложений (8)Alexander Gornik
 
NetworkUA - 2012 - Introduction TypeScript
NetworkUA - 2012 - Introduction TypeScript NetworkUA - 2012 - Introduction TypeScript
NetworkUA - 2012 - Introduction TypeScript Dmytro Mindra
 
Система обработки бизнес-логики server-side приложения на Groovy
Система обработки бизнес-логики server-side приложения на GroovyСистема обработки бизнес-логики server-side приложения на Groovy
Система обработки бизнес-логики server-side приложения на GroovyRegn
 
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полнойОмские ИТ-субботники
 
Java осень 2014 занятие 6
Java осень 2014 занятие 6Java осень 2014 занятие 6
Java осень 2014 занятие 6Technopark
 
Построение компилятора на базе LLVM — Павел Сычев
 Построение компилятора на базе LLVM — Павел Сычев Построение компилятора на базе LLVM — Павел Сычев
Построение компилятора на базе LLVM — Павел СычевYandex
 
Паттерны проектирования источников данных
Паттерны проектирования источников данныхПаттерны проектирования источников данных
Паттерны проектирования источников данныхAlex Polorotov
 
паттерны проектирования источников данных
паттерны проектирования источников данныхпаттерны проектирования источников данных
паттерны проектирования источников данныхVitaliy Trenkenshu
 

Similar to Roslyn API : SyntaxTree vs CodeDom, SemanticModel vs Reflection (20)

Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NETОпыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
 
Универсальный сигнатурный анализ кода на C#, Java, PHP
Универсальный сигнатурный анализ кода на C#, Java, PHPУниверсальный сигнатурный анализ кода на C#, Java, PHP
Универсальный сигнатурный анализ кода на C#, Java, PHP
 
Bytecode
BytecodeBytecode
Bytecode
 
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворковНикита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
 
kranonit S15 Vladimir Melnik - Ruby on Rails, BDD
kranonit S15 Vladimir Melnik - Ruby on Rails, BDDkranonit S15 Vladimir Melnik - Ruby on Rails, BDD
kranonit S15 Vladimir Melnik - Ruby on Rails, BDD
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-Studio
 
Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...
Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...
Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтеры
 
Как это работает: DLR
Как это работает: DLRКак это работает: DLR
Как это работает: DLR
 
Convert this: peculiarities of cross-platform mobile game development at Vizor
Convert this: peculiarities of cross-platform mobile game development at VizorConvert this: peculiarities of cross-platform mobile game development at Vizor
Convert this: peculiarities of cross-platform mobile game development at Vizor
 
Ciklum .NET Saturday - Introduction to TypeScript
Ciklum .NET Saturday - Introduction to TypeScriptCiklum .NET Saturday - Introduction to TypeScript
Ciklum .NET Saturday - Introduction to TypeScript
 
разработка бизнес приложений (8)
разработка бизнес приложений (8)разработка бизнес приложений (8)
разработка бизнес приложений (8)
 
NetworkUA - 2012 - Introduction TypeScript
NetworkUA - 2012 - Introduction TypeScript NetworkUA - 2012 - Introduction TypeScript
NetworkUA - 2012 - Introduction TypeScript
 
Система обработки бизнес-логики server-side приложения на Groovy
Система обработки бизнес-логики server-side приложения на GroovyСистема обработки бизнес-логики server-side приложения на Groovy
Система обработки бизнес-логики server-side приложения на Groovy
 
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
2014-10-04 02 Владислав Безверхий. Mocha - покрой frontend по полной
 
Java осень 2014 занятие 6
Java осень 2014 занятие 6Java осень 2014 занятие 6
Java осень 2014 занятие 6
 
Построение компилятора на базе LLVM — Павел Сычев
 Построение компилятора на базе LLVM — Павел Сычев Построение компилятора на базе LLVM — Павел Сычев
Построение компилятора на базе LLVM — Павел Сычев
 
Паттерны проектирования источников данных
Паттерны проектирования источников данныхПаттерны проектирования источников данных
Паттерны проектирования источников данных
 
паттерны проектирования источников данных
паттерны проектирования источников данныхпаттерны проектирования источников данных
паттерны проектирования источников данных
 

Roslyn API : SyntaxTree vs CodeDom, SemanticModel vs Reflection

  • 1. Roslyn API SyntaxTree vs CodeDom SemanticModel vs Reflection Денис Цветцих АстроСофт www.astrosoft.ru
  • 2. Почему это важно О Roslyn много говорят • Революция в мире компиляторов • Позволит развивать C# быстрее • OpenSource на GitHub • Nuget packages Microsoft.CodeAnalysis.* Что он даст для решения прикладных задач? • Анализ кода • Кодогенерация 2
  • 3. Почему я здесь Решал задачу кодогенерации • Roslyn: Syntax tree • CodeDom Решал задачу анализа кода • Roslyn: Semantic model • Reflection 3 Cравнение Roslyn с CodeDom и Reflection
  • 4. Опрос Кто решал задачи: • кодогенерации с помощью CodeDom? • анализа кода с помощью Reflection? Кто сделал хотя бы один сэмпл использования Roslyn? Кто применял Roslyn в своих проектах? 4
  • 5. План на сегодня • Возможности Roslyn для кодогенерации и анализа кода • Какие из них и почему мы использовали • Сравнение Roslyn с CodeDom и Reflection 5
  • 6. Какая стояла задача Клиент для ONVIF камеры на Windows Phone 8.1 • Работа с камерой через SOAP • Но Windows Phone 8.1 не поддерживает SOAP сервисы 6
  • 7. Решение: аналог “Add Service Reference” • Собственный класс SoapClientBase • По WSDL генерируем код утилитой SvcUtil • Анализируем код при помощи Reflection • Генерируем клиент SOAP сервиса при помощи CodeDom • Анализ кода – SemanticModel • Генерация кода – SyntaxTree 7
  • 8. Абстрактное синтактическое дерево Абстрактное синтактическое дерево (АСД, англ. Abstract Syntax Tree, AST) – древовидное представление синтаксической структуры программы Узлы – конструкции языка программирования (классы, методы, операторы) Листья – неделимые синтаксические конструкции (переменные, константы, ключевые слова) 8
  • 9. Пример SyntaxTree SyntaxNode: Синий SyntaxToken: Зеленый SyntaxTrivia: Красный 9 Console.WriteLine("Hello World");
  • 10. Создание SyntaxTree SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText("CODE"); Важно: SyntaxTree неизменяемо 10
  • 11. Решение задачи №1 • Строим AST для кода, сгенерированного SvcUtil для .NET • Перестраиваем с учетом специфики Windows Phone 8.1 • Сохраняем код перестроенного дерева 11
  • 12. CSharpSyntaxRewriter Реализация паттерна Visitor SyntaxVisitor.Visit(SyntaxNode node) SyntaxNode.Accept(SyntaxVisitor visitor) Перегрузка методов SyntaxNode Visit* SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node) SyntaxNode VisitInterfaceDeclaration(InterfaceDeclarationSyntax node) 12
  • 13. SyntaxRewriter: заменить тип на var 1 SyntaxNode VisitLocalDeclarationStatement 2 (LocalDeclarationStatementSyntax node) 3 { 4 var declarator = node.Declaration.Variables.First(); 5 TypeSyntax variableTypeName = node.Declaration.Type; 6 TypeSyntax varTypeName = SyntaxFactory.IdentifierName("var"); 7 return node.ReplaceNode(variableTypeName, varTypeName); 8 } 13
  • 14. Применение SyntaxRewriter + Незначительные точечные изменения кода - Значительные преобразования большого объема кода 14
  • 15. Решение задачи №2 • Строим AST для кода, сгенерированного SvcUtil для .NET • Анализируем AST. Получаем: • интерфейсы, помеченные [ServiceContract] • классы, помеченные [DataContract] • На основе анализа строим AST для Windows Phone 8.1 • Сохраняем код построенного дерева 15
  • 16. CSharpSyntaxWalker Тот же самый Visitor Перегрузка методов void Visit* void VisitClassDeclaration(ClassDeclarationSyntax node) void VisitInterfaceDeclaration(InterfaceDeclarationSyntax node) 16
  • 17. Использование SyntaxWalker 1 private readonly SemanticModel _semanticModel; 2 public readonly List<INamedTypeSymbol> Services; 3 4 override void VisitInterfaceDeclaration(InterfaceDeclarationSyntax node) 5 { 6 var symbol = _semanticModel.GetDeclaredSymbol(node); 7 if (symbol.GetAttributes() 8 .Any(attr => attr.AttributeClass.Name == "ServiceContractAttribute")) 9 Services.Add(symbol); 10 } 17
  • 18. API для кодогенерации • Наследники SyntaxNode имеют internal конструктор • Создание экземпляров при помощи SyntaxFactory • Редактирование при помощи Fluent API методов With* var decl = SyntaxFactory.ClassDeclaration("MyClass") .WithBaseList(baseList) .WithModifiers(modifiers) .WithAttributeLists(attributes); 18
  • 19. Создание namespace Как реализовано NameSyntax nameSyntax = SyntaxFactory.ParseName("MyNamespace"); NamespaceDeclarationSyntax ns = SyntaxFactory.NamespaceDeclaration(nameSyntax); Как хочется NamespaceDeclarationSyntax ns = SyntaxFactory.NamespaceDeclaration("MyNamespace"); 19
  • 20. Добавление директивы using Как реализовано NamespaceDeclarationSyntax ns = … var name = SyntaxFactory.ParseName("System"); var usingSyntax = SyntaxFactory.UsingDirective(name); ns = ns.AddUsings(usingSyntax); Как хочется ns = ns.AddUsings("System"); 20
  • 21. Создание поля класса Как реализовано TypeSyntax type = ... var variable = SyntaxFactory.VariableDeclarator("x"); var variableDecl = SyntaxFactory.VariableDeclaration(type); var variableList = SyntaxFactory.SeparatedList(new[] {variable}); variableDecl = variableDecl.WithVariables(variableList); var field = SyntaxFactory.FieldDeclaration(variableDecl); // private int x, y, z; // private int x; Как хочется var field = SyntaxFactory.FieldDeclaration("x", type); 21
  • 22. Можно добавить много, нельзя один • Переменная • Модификатор класса, метода, свойства, … • Реализуемые интерфейсы 22
  • 23. SyntaxTree.Parse* Преобразование строки в SyntaxNode var body = string.Format( "return this.CallAsync<{0}, {1}>("{2}", {3}.{4});", v1, v2, v3, v4, v5); SyntaxNode stmt = SyntaxFactory.ParseStatement(body); 23
  • 24. Workspace • MSBuildWorkspace – рабочая область • Solution – набор проектов • Project – проект, содержащий файлы исходного кода • Document – файл исходного кода, содержит SyntaxTree и соответствующую SemanticModel 24
  • 25. Форматирование var root = … // корень AST MSBuildWorkspace workspace = MSBuildWorkspace.Create(); var formattedRoot = Formatter.Format(root, workspace); File.WriteAllText("code.cs", formattedRoot.ToFullString()); 25
  • 26. Syntax API для кодогенерации • Предназначено для решения общих задач • Добавить классу список модификаторов • API для решения частных задач отсутствует • Нельзя добавить классу один модификатор • Многословно • Создать название, а потом уже namespace • Решение – extension методы 26
  • 27. Extension метод: добавить модификатор(ы) 1 public static PropertyDeclarationSyntax WithModifiers 2 (this PropertyDeclarationSyntax propertySyntax, 3 params SyntaxKind[] modifiers) 4 { 5 var tokenList = new SyntaxTokenList(); 6 foreach (var modifier in modifiers) 7 { 8 tokenList = tokenList.Add(SyntaxFactory.Token(modifier)); 9 } 10 return propertySyntax.WithModifiers(tokenList); 11 } 27
  • 28. SyntaxTree vs CodeDom SyntaxTree ± Нет API для решения частных задач (extension method) ± Сразу генерируется код на C# + Наличие методов Parse* CodeDom + Есть API для решения частных задач + Строится модель, по ней можно сгенерить C# и VB 28
  • 29. Анализ кода Compilation – аналог проекта. Содержит: • Список элементов для компиляции • Assembly references 29
  • 30. Создание CSharpCompilation SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText(csFilePath)); var mscorlib = MetadataReference.CreateFromAssembly(typeof(object).Assembly); var compilation = CSharpCompilation.Create("ServiceReference", new[] { syntaxTree }, new[] { mscorlib }); var semanticModel = compilation.GetSemanticModel(syntaxTree); 30
  • 31. SemanticModel Extension метод GetDeclaredSymbol: по узлу SyntaxTree получить соответствующий семантический объект IPropertySymbol GetDeclaredSymbol(PropertyDeclarationSyntax syntax) INamedTypeSymbol GetDeclaredSymbol(BaseTypeDeclarationSyntax syntax) 31
  • 32. Получение свойств класса Как реализовано ITypeSymbol typeSymbol = … var properties = typeSymbol .GetMembers() .Where(m => m.Kind == SymbolKind.Property) .Cast<IPropertySymbol>(); Как хочется var properties = typeSymbol.GetProperties(); 32
  • 33. Является ли тип примитивом Стандартной реализации нет. Можно реализовать так: ITypeSymbol type; var primitives = new HashSet<string> { "byte", "int", ext. }; bool isPrimitive = primitives.Contains(type.ToString()); 33
  • 34. Анализ атрибутов [XmlElement("elementName", typeof(Test))] [XmlElement("elementName", Type = typeof(Test))] public XmlElementAttribute(){} public XmlElementAttribute(string elementName){} public XmlElementAttribute(Type type){} public XmlElementAttribute(string elementName, Type type){} 34
  • 35. Reflection: параметр Type var type = propertyInfo .GetCustomAttribute<XmlElementAttribute>() .Type; И все!  35
  • 36. SemanticModel: AttributeData ImmutableArray<TypedConstant> ConstructorArguments { get; } ImmutableArray<KeyValuePair<string, TypedConstant>> NamedArguments { get; } 36
  • 37. Ищем Type в NamedArguments 1 AttributeData attribute = … // анализируемый атрибут 2 TypedConstant? dataType; // результат 3 4 var named = attr.NamedArguments 5 .FirstOrDefault(item => item.Key == "Type"); 6 if (!named.Equals(default(KeyValuePair<string, TypedConstant>))) 7 dataType = named.Value; 8 else // будем искать в параметрах конструктора 9 Продолжение на следующем слайде 37
  • 38. Ищем Type в ConstructorArguments 9 if (attribute.ConstructorArguments.Length == 2) 10 dataType = attribute.ConstructorArguments.Last(); 11 else 12 { 13 var arg = attribute.ConstructorArguments.FirstOrDefault(); 14 if (!arg.Equals(default(TypedConstant)) && 15 arg.Type.Name == "Type") //"Type" – System.Type 16 dataType = arg; 17 } 38
  • 39. Собираем все вместе 1 AttributeData attribute = … // анализируемый атрибут 2 TypedConstant? dataType; // результат 3 4 var named = attr.NamedArguments.FirstOrDefault(item => item.Key == "Type"); 5 if (!named.Equals(default(KeyValuePair<string, TypedConstant>))) dataType = named.Value; 6 else 7 if (attribute.ConstructorArguments.Length == 2) 8 dataType = attribute.ConstructorArguments.Last(); 9 else { 11 var arg = attribute.ConstructorArguments.FirstOrDefault(); 12 if (!arg.Equals(default(TypedConstant)) && arg.Type.Name == "Type") 13 dataType = arg; 14 } 39
  • 40. SemanticModel для анализа кода • Предназначена для решения общих задач • Получить список членов класса • Получить список элементов namespace • API для решения частных задач отсутствует • Нельзя получить отдельный список свойств класса • Нельзя получить список классов namespace • Нет проверки примитивности типа • Сложная работа с атрибутами 40
  • 41. Extension метод: получить свойства класса 1 public static IEnumerable<IPropertySymbol> 2 GetProperties(this ITypeSymbol typeSymbol) 3 { 4 return typeSymbol 5 .GetMembers() 6 .Where(m => m.Kind == SymbolKind.Property) 7 .Cast<IPropertySymbol>(); 8 } 41
  • 42. SemanticModel vs Reflection SemanticModel ± Нет API для решения частных задач (extension method) - Нет проверки примитивности типа - Сложное получение параметров атрибутов Reflection + Есть API для решения частных задач + Есть проверка примитивности типа + Простое получение параметров атрибутов 42
  • 43. Впечатление от Roslyn API Мне хотелось бы видеть Roslyn API таким, что: • Позволяет быстро решать простые задачи • Допускает решение сложных задач 43
  • 44. Заключение SyntaxTree • SyntaxRewriter – для точечных изменений • Многословность (создать имя, а потом сам namespace) • Решает общие задачи (нельзя добавить один модификатор) • Есть удобные методы Parse* • Форматирование в пакете Workspace SemanticModel • Решает общие задачи (нельзя получить свойства класса) • Нет проверки типа на примитивность • Неудобная работа с атрибутами 44
  • 45. Спасибо за внимание Денис Цветцих den.tsvettsih@yandex.ru

Editor's Notes

  1. О Roslyn много говорят, отзывы положительные. Да, он поможет быстрее развивать C#. Кроме прочего, код Roslyn открыт, и его функционал поставляется в виде пакетов. Но что он даст широкому кругу разработчиков для решения прикладных задач, например кодогенерации и анализа кода Это не такие распространенные задачи, как разработка справочников, но все же они боле актуальны, чем прикручивание новых фичей к C#
  2. Я – послание, Я проходил через, я научился, я хочу чтобы вы
  3. Кто решал задачи: для вас этот доклад может оказаться полезным, потому, что вы узнаете что-то новое. Вы увидите, как те же самые задачи можно решить с помощью другого инструмента. У вас расширится кругозор. И в следующий раз вы можете применить Roslyn для решения тех же задач. Кто сделал сэмпл: я расскажу о нашем опыте и о тех граблях, которые мы собрали., чтобы вы на них уже не наступили У кого реальный опыт: мы можем обменяться опытом, а возможно вы даже чем-то меня дополните.
  4. Так ребята из MS ненавязчиво намекают, что SOAP – это не тренд. Особенно при разработке мобильных приложений. Проблема: в фреймворке нет API, которое нужно для решения нашей задачи
  5. Картинка?
  6. Для других языков уже было реализовано. Например для питона и Java. Сейчас есть и для C#
  7. Сказать про неизменяемость синтаксического дерева. Это позволяет одновременно анализировать синтаксическое дерево несколькими потоками
  8. Позволяет решать простые задачи, вносить точечные правки Пример – заменить имена классов в объявлении переменных на var Для ситуации, когда код приходится перефигачивать очень сильно, он не подходит
  9. Просто обход дерева без его изменения. Пример – выбор дата контрактов и ServiceContract
  10. Возможно стоит заменить на названия типов на var, чтобы влезало в одну строку
  11. Возможно стоит заменить на названия типов на var, чтобы влезало в одну строку
  12. Пример достаточно показателен. Нельзя добавить классу один модификатор. Их можно добавить несколько. Нельзя добавить один базовый класс, их можно добавить несколько. Нужны ли примеры базовых классов и модификаторов
  13. Полезно для разработчиков плагинов к студии. Но содержит полезную для кодогенерации фичу – форматирование. Мы её как-то не сразу нашли, поэтому я о ней говорю, чтобы вы о ней знали и могли использовать.
  14. Этот код не надо читать, здесь весь пример собран воедино Кода много, и он сложный. Самое плохое – он подходит только для атрибута XmlElement. Для другого атрибута этот код придется переписывать. А если атрибуту добавить или удалить конструкторов, этот код сломается.
  15. Колбаса нравится не только из-за вкуса, но и из-за того, насколько хорошо она чистится.
  16. Надеюсь, этот доклад оказался для вас полезным, и породил у вас какие-то практические мысли. Если у вас есть какие-то вопросы, буду рад на них ответить Принцип 48 часов: чтобы информация не забылась, нужно что-то сделать. Поиграйтесь с 2015 студией, попробуйте визуализировать SyntaxTree для вашего кода Скачайте и посмотрите сэмплы, напишите простейший пример использовани Roslyn: как поменять название типа на var Занесите Issue в GitHub. Сами их реализуйте, сделайте Pull Request докажите насколько вы крутой программист.