Introducing C# Source Generators
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 01.05.20 12:06
Оценка: 147 (8)
https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/
Как то новость прошла без энтузиазма

Очень интересен пример AutoNotifyGenerator https://github.com/dotnet/roslyn-sdk/blob/master/samples/CSharp/SourceGenerators/SourceGeneratorSamples/AutoNotifyGenerator.cs
Пример использования атрибутов
https://github.com/dotnet/roslyn-sdk/blob/master/samples/CSharp/SourceGenerators/GeneratedDemo/UseAutoNotifyGenerator.cs
Раньше использовались Fody Refit которые Il код редактировали.
Ну и T4, правда бы еще и синтаксис типа разора

Так же собираются добавить partial методы отличных от void, ref параметры, private
https://github.com/jaredpar/csharplang/blob/partial/proposals/extending-partial-methods.md

partial on all members
Given that we're expanding partial to be more friendly to source generators should we also expand it to work on all class members? For example should we be able to declare partial constructors, operators, etc ...
Resolution The idea is sound but at this point in the C# 9 schedule we're trying to avoid unnecessary feature creep. Want to solve the immediate problem of expanding the feature to work with modern source generators.
Extending partial to support other members will be considered for the C# 10 release. Seems likely that we will consider this extension.



Еще
New C# Source Generator Samples
и солнце б утром не вставало, когда бы не было меня
Отредактировано 26.08.2020 9:03 Serginio1 . Предыдущая версия . Еще …
Отредактировано 26.05.2020 12:01 Serginio1 . Предыдущая версия .
Отредактировано 01.05.2020 13:36 Serginio1 . Предыдущая версия .
Re: Introducing C# Source Generators
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.05.20 14:05
Оценка: 39 (3)
Здравствуйте, Serginio1, Вы писали:

S>https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/

S>Как то новость прошла без энтузиазма
Крутая штука. Надо покрутить.
S> Очень интересен пример AutoNotifyGenerator https://github.com/dotnet/roslyn-sdk/blob/master/samples/CSharp/SourceGenerators/SourceGeneratorSamples/AutoNotifyGenerator.cs
Cходу не понял, нафига они инжектят код атрибута. Такие-то штуки можно просто через Dependency принести, обычным атрибутом как у всех.
Или они хотели избежать необходимости давать ссылку на второй пакет?

S> Раньше использовались Fody Refit которые Il код редактировали.

S>Ну и T4, правда бы еще и синтаксис типа разора

У меня пока есть идея улучшить рефлексию. При работе с ExpressionTrees и ILGenerator адский объём кода уходит на все эти typeof(MemoryMarshal).GetRuntimeMethods().Where(x => x.Name.Equals("Cast")).Select(m => new { Method = m, Parameters = m.GetParameters() }).FirstOrDefault(p =>p.Parameters.Length == 1 && p.Parameters[0].ParameterType.IsGenericType && p.Parameters[0].ParameterType.GetGenericTypeDefinition() == typeof(Span<>)).
Не дожидаясь infoof() (ах, где же ты теперь, блог Липперта...), люди воротят полурешения.
Половинные они потому, что внезапно есть целая куча ограничений — например, метод, возвращающий банальный Span<int>, таким образом не получишь (CS8640). А ещё есть CS8153 и много его друзей.
А так мы можем для интересного нам кода генерировать type class, который для каждого мембера будет иметь одноимённую проперть или метод — типа
public class MemoryMarshalType
{ 
   public static MethodInfo Cast_SpanT<T, R>() 
   public static MethodInfo Cast_ReadonlySpanT<T,R>()
}

public class SpanType<T>
{
   public static PropertyInfo Item{get;}
   public static PropertyInfo Empty { get;} 
   public static PropertyInfo Length { get;}
}

В итоге, вместо
il1.Emit(OpCodes.Call, typeof(Console).GetMethod("Write", new Type[] { typeof(string) }));

пишем
il1.Emit(OpCodes.Call, ConsoleType.Write_String);

(Пока ещё не уверен, что это лучший способ).
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 05.05.2020 14:02 Sinclair . Предыдущая версия .
Re[2]: Introducing C# Source Generators
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 01.05.20 14:14
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>>https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/

S>>Как то новость прошла без энтузиазма
S>Крутая штука. Надо покрутить.
S>> Очень интересен пример AutoNotifyGenerator https://github.com/dotnet/roslyn-sdk/blob/master/samples/CSharp/SourceGenerators/SourceGeneratorSamples/AutoNotifyGenerator.cs
S>Cходу не понял, нафига они инжектят код атрибута. Такие-то штуки можно просто через Dependency принести, обычным атрибутом как у всех.
S>Или они хотели избежать необходимости давать ссылку на второй пакет?
https://github.com/dotnet/roslyn-sdk/blob/master/samples/CSharp/SourceGenerators/GeneratedDemo/UseAutoNotifyGenerator.cs
Ну они генерят отдельный файл во время компиляции, при этом можно использовать внутри кода
 public partial class ExampleViewModel

    {

        [AutoNotify]

        private string _text = "private field text";



        [AutoNotify(PropertyName = "Count")]

        private int _amount = 5;

    }



    public static class UseAutoNotifyGenerator

    {

        public static void Run()

        {

            ExampleViewModel vm = new ExampleViewModel();



            // we didn't explicitly create the 'Text' property, it was generated for us 

            string text = vm.Text;

            Console.WriteLine($"Text = {text}");



            // Properties can have differnt names generated based on the PropertyName argument of the attribute

            int count = vm.Count;

            Console.WriteLine($"Count = {count}");



            // the viewmodel will automatically implement INotifyPropertyChanged

            vm.PropertyChanged += (o, e) => Console.WriteLine($"Property {e.PropertyName} was changed");

            vm.Text = "abc";

            vm.Count = 123;



            // Try adding fields to the ExampleViewModel class above and tagging them with the [AutoNotify] attribute

            // You'll see the matching generated properties visibile in IntelliSense in realtime

        }
и солнце б утром не вставало, когда бы не было меня
Re: Introducing C# Source Generators
От: Слава  
Дата: 01.05.20 14:24
Оценка: 3 (1) +3 :)))
Здравствуйте, Serginio1, Вы писали:

S>Ну и T4, правда бы еще и синтаксис типа разора


<<<комментарий про Nemerle>>>
Re[2]: Introducing C# Source Generators
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 01.05.20 14:30
Оценка: -1
Здравствуйте, Слава, Вы писали:

С><<<комментарий про Nemerle>>>


Угу
и солнце б утром не вставало, когда бы не было меня
Отредактировано 01.05.2020 14:31 Serginio1 . Предыдущая версия .
Re: Introducing C# Source Generators
От: Osaka  
Дата: 01.05.20 15:50
Оценка:
S>https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/
S>новость прошла без энтузиазма

Generate C# source files that can be added to a Compilation object during the course of compilation. In other words, you can provide additional source code as input to a compilation while the code is being compiled

Оно в овеществлённом виде появляется? (Файлом в проекте). Туда пошаговый отладчик заходит?
Re: Introducing C# Source Generators
От: hi_octane Беларусь  
Дата: 01.05.20 17:23
Оценка: +2
S>Ну и T4, правда бы еще и синтаксис типа разора
В текущем виде это фича "на от№*бись". Явно у менджеров на собрании было: "в Go есть генераторы — нам тоже надо". Любая связка Razor + Roslyn может больше (например менять существующие файлы с кодом), при этом синтаксис разор гораздо читабельнее, чем конкатенация строк.
Re[2]: Introducing C# Source Generators
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 01.05.20 17:36
Оценка:
Здравствуйте, Osaka, Вы писали:


O>Оно в овеществлённом виде появляется? (Файлом в проекте). Туда пошаговый отладчик заходит?

Пока не смотрел.
https://github.com/dotnet/roslyn-sdk/blob/master/samples/CSharp/SourceGenerators/SourceGeneratorSamples/AutoNotifyGenerator.cs

По коду

 // group the fields by class, and generate the source

            foreach (IGrouping<INamedTypeSymbol, IFieldSymbol> group in fieldSymbols.GroupBy(f => f.ContainingType))

            {

                string classSource = ProcessClass(group.Key, group.ToList(), attributeSymbol, notifySymbol, context);

               context.AddSource($"{group.Key.Name}_autoNotify.cs", SourceText.From(classSource, Encoding.UTF8));

            }


Должны создаваться файлы
и солнце б утром не вставало, когда бы не было меня
Re[2]: Introducing C# Source Generators
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 01.05.20 17:38
Оценка:
Здравствуйте, hi_octane, Вы писали:

S>>Ну и T4, правда бы еще и синтаксис типа разора

_>В текущем виде это фича "на от№*бись". Явно у менджеров на собрании было: "в Go есть генераторы — нам тоже надо". Любая связка Razor + Roslyn может больше (например менять существующие файлы с кодом), при этом синтаксис разор гораздо читабельнее, чем конкатенация строк.

Про синтаксис понятно. Здесь генерация с помощью Рослина за счет доступа к контексту.
и солнце б утром не вставало, когда бы не было меня
Re[2]: Introducing C# Source Generators
От: _NN_ www.nemerleweb.com
Дата: 01.05.20 19:07
Оценка:
Здравствуйте, hi_octane, Вы писали:

S>>Ну и T4, правда бы еще и синтаксис типа разора

_>В текущем виде это фича "на от№*бись". Явно у менджеров на собрании было: "в Go есть генераторы — нам тоже надо". Любая связка Razor + Roslyn может больше (например менять существующие файлы с кодом), при этом синтаксис разор гораздо читабельнее, чем конкатенация строк.

Как не звучало бы грустно, но люди в Go в восторге от этой ручной обработки строк вместо нормальной работы с синтаксическим деревом.
К тому же это ведь кажется правильно и всем легко понять что делает код когда генерируется пара строк.

Правильней было бы оформить API компилятора ну это мы и так знаем
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[3]: Introducing C# Source Generators
От: hi_octane Беларусь  
Дата: 01.05.20 20:12
Оценка: 114 (1) +1
S> Про синтаксис понятно. Здесь генерация с помощью Рослина за счет доступа к контексту.
Так и в моих проектах генерация при помощи Рослина с полным доступом к сорцам, референсам, и т.д. Как вишенка на торте — после генерации разором отрабатывает ещё и форматтер который сгенерированные файлы причёсывает в полном соответствии с нашим кодинг-стандартом. Вряд ли об этом подумали в source generators, кстати

Но в моём решении есть и минус — проход таска "генерация" занимает столько же времени сколько и компиляция (грузится весь солюшн, проход по всем проектам и файлам в них), поэтому его на каждый чих запускать напряжно. У source generators этой проблемы не будет.
Re[2]: Introducing C# Source Generators
От: hi_octane Беларусь  
Дата: 01.05.20 20:18
Оценка: 4 (1)
O>Оно в овеществлённом виде появляется? (Файлом в проекте). Туда пошаговый отладчик заходит?
Из FAQ:

Can I debug or navigate to generated source in Visual Studio?
Eventually, we’ll support navigation and debugging of generated source in Visual Studio. It is not yet supported in this early preview stage.


Получается пока хуже чем генерировать сорец через T4 или Razor.
Отредактировано 01.05.2020 20:18 hi_octane . Предыдущая версия .
Re[3]: Introducing C# Source Generators
От: Sinclair Россия https://github.com/evilguest/
Дата: 02.05.20 03:00
Оценка:
Здравствуйте, Serginio1, Вы писали:
S>https://github.com/dotnet/roslyn-sdk/blob/master/samples/CSharp/SourceGenerators/GeneratedDemo/UseAutoNotifyGenerator.cs
S> Ну они генерят отдельный файл во время компиляции, при этом можно использовать внутри кода

Вот я и спрашиваю, что помешало им этот атрибут объявить в какой-то сборке, на которую будет депендиться пользовательский проект.
Генераторы необходимы для динамической генерации кода — т.е. когда что-то зависит от того кода, который ужо есть в проекте. А статику-то зачем порождать?
Может, в качестве иллюстрации только...
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Introducing C# Source Generators
От: _NN_ www.nemerleweb.com
Дата: 02.05.20 06:19
Оценка:
Здравствуйте, hi_octane, Вы писали:

Пользуясь случаем хочу передать привет бабушке каким форматотером пользуетесь для C# ?
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[4]: Introducing C# Source Generators
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 02.05.20 08:05
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:

S>>https://github.com/dotnet/roslyn-sdk/blob/master/samples/CSharp/SourceGenerators/GeneratedDemo/UseAutoNotifyGenerator.cs
S>> Ну они генерят отдельный файл во время компиляции, при этом можно использовать внутри кода

S>Вот я и спрашиваю, что помешало им этот атрибут объявить в какой-то сборке, на которую будет депендиться пользовательский проект.

S>Генераторы необходимы для динамической генерации кода — т.е. когда что-то зависит от того кода, который ужо есть в проекте. А статику-то зачем порождать?
S>Может, в качестве иллюстрации только...

Ну это для примера. То, что можно и атрибуты генерить и их использовать. Круто!


  [Generator]
    public class AutoNotifyGenerator : ISourceGenerator
    {
        private const string attributeText = @"
using System;
namespace AutoNotify
{
    [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
    sealed class AutoNotifyAttribute : Attribute
    {
        public AutoNotifyAttribute()
        {
        }
        public string PropertyName { get; set; }
    }
}
";

        public void Initialize(InitializationContext context)
        {
            // Register a syntax receiver that will be created for each generation pass
            context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
        }

        public void Execute(SourceGeneratorContext context)
        {
            // add the attribute text
            context.AddSource("AutoNotifyAttribute", SourceText.From(attributeText, Encoding.UTF8));
и солнце б утром не вставало, когда бы не было меня
Re[2]: Introducing C# Source Generators
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 02.05.20 08:20
Оценка: +2
Здравствуйте, hi_octane, Вы писали:

S>>Ну и T4, правда бы еще и синтаксис типа разора

_>В текущем виде это фича "на от№*бись". Явно у менджеров на собрании было: "в Go есть генераторы — нам тоже надо".

Ну... Я бы тут поспорил.
Ребята предложили базовый механизм, который:
— получает на вход текущий контекст компиляции
— каким-то (они нигде не ограничивают каким именно) образом генерируют добавочный код, который встраивается в общий объем компилируемого кода

_>Любая связка Razor + Roslyn может больше (например менять существующие файлы с кодом), при этом синтаксис разор гораздо читабельнее, чем конкатенация строк.

Ну про то, что rewriting не доступен это да, но они ведь и везде подчеркивают этот момент — это куда более простой механизм именно для генерации кода.
А если вам нравится именно Razor-овский синтаксис, так вы спокойно можете его использовать — просто из генератора будете вызывать привычный вам шаблонизатор: Razor, T4, Liquid, .... Или вообще не шаблонизатор, а какой-нибудь CodeDOM-генератор...
А весь Roslyn-овский контекст (готовая модель) у вас при этом будет.

P.S. Я, кстати, плохо представляю, как Razor использовать для генерации C# кода.
Вы пробовали? Как впечатления? Можете поделиться?
Re[4]: Introducing C# Source Generators
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 02.05.20 08:31
Оценка: +2
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:

S>>https://github.com/dotnet/roslyn-sdk/blob/master/samples/CSharp/SourceGenerators/GeneratedDemo/UseAutoNotifyGenerator.cs
S>> Ну они генерят отдельный файл во время компиляции, при этом можно использовать внутри кода

S>Вот я и спрашиваю, что помешало им этот атрибут объявить в какой-то сборке, на которую будет депендиться пользовательский проект.

Подозреваю, что 2 вещи:
— как вы сказали выше, нет необходимости отдельно подключать еще одну сборку. Вроде как NuGet позволяет это упростить (т.е. одну сборку подключить в рантайм, а вторую — как tools для компилятора), поэтому не так чтобы сильно напрягало, но как минимум нужен еще 1 инструмент (NuGet) — а тут достаточно 1 сборки.
— дополнительная защита от ошибок. Т.е. сборка с атрибутом подключилась, а генератор почему-то не отработал (забыли указать в настройках, например) — и вы об этом никак не узнаете. А здесь у вас сразу отвалится объявление атрибута и компилятор упадет с ошибкой.
Re[3]: Introducing C# Source Generators
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 02.05.20 09:19
Оценка: +2 -1
Здравствуйте, hi_octane, Вы писали:

_>Получается пока хуже чем генерировать сорец через T4 или Razor.

Это же только проба пера

We expect that Source Generators will obviate T4, which unfortunately does depend on the MSBuild trickery mentioned in the post. The current requirement to use strings is just the first preview. Although it is the most flexible approach, it’s also the most rudimentary. We’ll likely explore some additional approaches, such as using the SyntaxFactory APIs.

и солнце б утром не вставало, когда бы не было меня
Re: Introducing C# Source Generators
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 02.05.20 11:25
Оценка: 18 (1)
Здравствуйте, Serginio1, Вы писали:

S>Раньше использовались Fody Refit которые Il код редактировали.

S>Ну и T4, правда бы еще и синтаксис типа разора

Всё-таки, мне кажется, не вполне правильным сравнивать данный механизм c Fody или другими code rewriters.

Это именно генератор. Точнее даже так — это механизм для создания генераторов.
Что он дает:
— доступ к результатам анализа существующего кода (кстати, отдельно оговаривается, что туда не попадают результаты работы других генераторов)
— возможность на ходу добавить сгенерированный каким-либо способом код к исходному, используя чисто возможности компилятора.

Т.е. это в большей степени механизм, который позволяет отказаться для кодогенераторов в compile-time от костылей на базе msbuild
Автор: Михаил Романов
Дата: 06.07.18
.

А уж как вы будете реализовывать вашу кодогенерацию — решать вам. А там ведь куча вариантов:
— что брать за основу (модель) для генератора: ничего (т.е. вы тупо добавляете один и тот же код — не знаю зачем, но вдруг), исходный код, разобранный Roslyn (как в примере с атрибутом), или какой-то сторонний DSL (на базе JSON, XAML, ...)
— как получать фрагменты кода для добавления: на базе шаблонов (T4, Razor, Liquid, ...), через CodeDOM, склейкой строк, через модель Roslyn, ...

При этом (как минимум в теории, но надеюсь и на практике всё получится) — у вас всё сразу должно подхватываться в IDE.

P.S. Ну и как видите в таком варианте (замена для MSBuild generator) это не что-то особо новое и прекрасное. Просто упрощение для одной из задач. Не самой распространенной.
Поэтому, возможно, и нет особого хайпа.
Re[5]: Introducing C# Source Generators
От: hi_octane Беларусь  
Дата: 02.05.20 12:09
Оценка:
_NN>Пользуясь случаем хочу передать привет бабушке каким форматотером пользуетесь для C# ?
Рослином и пользуюсь. Чистая копипаста из примеров использования:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.CSharp.Formatting;
using System.Text;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.CSharp;

    private static AdhocWorkspace _formatWorkspace;
        private static OptionSet _formatOptions;


    _formatWorkspace = new AdhocWorkspace();
        _formatOptions = _formatWorkspace.Options;
        _formatOptions = _formatOptions.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInMethods, true);
        _formatOptions = _formatOptions.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInProperties, true);
        _formatOptions = _formatOptions.WithChangedOption(CSharpFormattingOptions.SpaceAfterControlFlowStatementKeyword, false);
        _formatOptions = _formatOptions.WithChangedOption(CSharpFormattingOptions.NewLineForCatch, false);
        _formatOptions = _formatOptions.WithChangedOption(CSharpFormattingOptions.NewLineForFinally, false);

    static string FormatFile(string content)
        {
            var syntaxNode = SyntaxFactory.ParseCompilationUnit(content);
            var formattedNode = Formatter.Format(syntaxNode, _formatWorkspace, _formatOptions);
            var sb = new StringBuilder();
            using(var writer = new StringWriter(sb))
            {
                formattedNode.WriteTo(writer);
            }
            return sb.ToString();
        }
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.