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.
У меня пока есть идея улучшить рефлексию. При работе с 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);
(Пока ещё не уверен, что это лучший способ).
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
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 attributeint 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
}
и солнце б утром не вставало, когда бы не было меня
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
Оно в овеществлённом виде появляется? (Файлом в проекте). Туда пошаговый отладчик заходит?
S>Ну и T4, правда бы еще и синтаксис типа разора
В текущем виде это фича "на от№*бись". Явно у менджеров на собрании было: "в Go есть генераторы — нам тоже надо". Любая связка Razor + Roslyn может больше (например менять существующие файлы с кодом), при этом синтаксис разор гораздо читабельнее, чем конкатенация строк.
// group the fields by class, and generate the sourceforeach (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));
}
Должны создаваться файлы
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, hi_octane, Вы писали:
S>>Ну и T4, правда бы еще и синтаксис типа разора _>В текущем виде это фича "на от№*бись". Явно у менджеров на собрании было: "в Go есть генераторы — нам тоже надо". Любая связка Razor + Roslyn может больше (например менять существующие файлы с кодом), при этом синтаксис разор гораздо читабельнее, чем конкатенация строк.
Про синтаксис понятно. Здесь генерация с помощью Рослина за счет доступа к контексту.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, hi_octane, Вы писали:
S>>Ну и T4, правда бы еще и синтаксис типа разора _>В текущем виде это фича "на от№*бись". Явно у менджеров на собрании было: "в Go есть генераторы — нам тоже надо". Любая связка Razor + Roslyn может больше (например менять существующие файлы с кодом), при этом синтаксис разор гораздо читабельнее, чем конкатенация строк.
Как не звучало бы грустно, но люди в Go в восторге от этой ручной обработки строк вместо нормальной работы с синтаксическим деревом.
К тому же это ведь кажется правильно и всем легко понять что делает код когда генерируется пара строк.
Правильней было бы оформить API компилятора ну это мы и так знаем
S> Про синтаксис понятно. Здесь генерация с помощью Рослина за счет доступа к контексту.
Так и в моих проектах генерация при помощи Рослина с полным доступом к сорцам, референсам, и т.д. Как вишенка на торте — после генерации разором отрабатывает ещё и форматтер который сгенерированные файлы причёсывает в полном соответствии с нашим кодинг-стандартом. Вряд ли об этом подумали в source generators, кстати
Но в моём решении есть и минус — проход таска "генерация" занимает столько же времени сколько и компиляция (грузится весь солюшн, проход по всем проектам и файлам в них), поэтому его на каждый чих запускать напряжно. У source generators этой проблемы не будет.
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.
Вот я и спрашиваю, что помешало им этот атрибут объявить в какой-то сборке, на которую будет депендиться пользовательский проект.
Генераторы необходимы для динамической генерации кода — т.е. когда что-то зависит от того кода, который ужо есть в проекте. А статику-то зачем порождать?
Может, в качестве иллюстрации только...
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, 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));
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, hi_octane, Вы писали:
S>>Ну и T4, правда бы еще и синтаксис типа разора _>В текущем виде это фича "на от№*бись". Явно у менджеров на собрании было: "в Go есть генераторы — нам тоже надо".
Ну... Я бы тут поспорил.
Ребята предложили базовый механизм, который:
— получает на вход текущий контекст компиляции
— каким-то (они нигде не ограничивают каким именно) образом генерируют добавочный код, который встраивается в общий объем компилируемого кода
_>Любая связка Razor + Roslyn может больше (например менять существующие файлы с кодом), при этом синтаксис разор гораздо читабельнее, чем конкатенация строк.
Ну про то, что rewriting не доступен это да, но они ведь и везде подчеркивают этот момент — это куда более простой механизм именно для генерации кода.
А если вам нравится именно Razor-овский синтаксис, так вы спокойно можете его использовать — просто из генератора будете вызывать привычный вам шаблонизатор: Razor, T4, Liquid, .... Или вообще не шаблонизатор, а какой-нибудь CodeDOM-генератор...
А весь Roslyn-овский контекст (готовая модель) у вас при этом будет.
P.S. Я, кстати, плохо представляю, как Razor использовать для генерации C# кода.
Вы пробовали? Как впечатления? Можете поделиться?
Здравствуйте, 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 сборки.
— дополнительная защита от ошибок. Т.е. сборка с атрибутом подключилась, а генератор почему-то не отработал (забыли указать в настройках, например) — и вы об этом никак не узнаете. А здесь у вас сразу отвалится объявление атрибута и компилятор упадет с ошибкой.
Здравствуйте, 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.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>Раньше использовались Fody Refit которые Il код редактировали. S>Ну и T4, правда бы еще и синтаксис типа разора
Всё-таки, мне кажется, не вполне правильным сравнивать данный механизм c Fody или другими code rewriters.
Это именно генератор. Точнее даже так — это механизм для создания генераторов.
Что он дает:
— доступ к результатам анализа существующего кода (кстати, отдельно оговаривается, что туда не попадают результаты работы других генераторов)
— возможность на ходу добавить сгенерированный каким-либо способом код к исходному, используя чисто возможности компилятора.
Т.е. это в большей степени механизм, который позволяет отказаться для кодогенераторов в compile-time от костылей на базе msbuild
А уж как вы будете реализовывать вашу кодогенерацию — решать вам. А там ведь куча вариантов:
— что брать за основу (модель) для генератора: ничего (т.е. вы тупо добавляете один и тот же код — не знаю зачем, но вдруг), исходный код, разобранный Roslyn (как в примере с атрибутом), или какой-то сторонний DSL (на базе JSON, XAML, ...)
— как получать фрагменты кода для добавления: на базе шаблонов (T4, Razor, Liquid, ...), через CodeDOM, склейкой строк, через модель Roslyn, ...
При этом (как минимум в теории, но надеюсь и на практике всё получится) — у вас всё сразу должно подхватываться в IDE.
P.S. Ну и как видите в таком варианте (замена для MSBuild generator) это не что-то особо новое и прекрасное. Просто упрощение для одной из задач. Не самой распространенной.
Поэтому, возможно, и нет особого хайпа.
_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();
}