Re[9]: Новости C#12
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 14.11.23 16:27
Оценка:
Здравствуйте, Silver_S, Вы писали:

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


G>>С генераторами и интерсепторами можно переписать так:

G>>
G>>[CompiledQuery]
G>>public static IAsyncEnumerable<Blog> GetBlogs(this BloggingContext context, int length)
G>>


G>>Генератор соберет нужный метод-инерсептор и подставить его вместо вызова GetBlogs. Тогда с этим прекрасно сможет работать AOT.


S_S>Только непонятно — зачем здесь перехватывать вызовы старого GetBlogs и перенаправлять, если можно напрямую вызывать сразу правильный метод GetBlogs созданный генератором.

S_S>Тем более что генераторы создают методы даже не в compile-time, а раньше, в edit-time, как только навесишь атрибут сразу появится исправленный метод.

Кстати зачем нужен генератор и почему нельзя просто "подставить":

Если есть nullable параметры запроса, то надо генерировать два запроса когда параметр null и когда не null

Например
from v in context.Vacancy
where v.Salary == salary
select ...

Где salaly — nullable

Тогда для GetVacancy(int? salary) надо генерировать два разных вызова когда null и когда не-null. Генераторы в C# легко эту задачу решат, так как анализирую реальные вызовы в исходнике. А edit-time генерация не факт что поможет
Re[6]: Новости C#12
От: IT Россия linq2db.com
Дата: 15.11.23 04:55
Оценка:
Здравствуйте, gandjustas, Вы писали:

IT>>Ага, не ппонятно какие тут могут быть сценарии использования.

G>Например compiletime кодогенерация вместо разбора Expression Tree в рантайме.

Например?
Если нам не помогут, то мы тоже никого не пощадим.
Re: Новости C#12
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 15.11.23 09:41
Оценка: 9 (1)
Announcing .NET 8
Download .NET 8 today!
и солнце б утром не вставало, когда бы не было меня
Re[8]: Новости C#12
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 15.11.23 09:58
Оценка:
Здравствуйте, Silver_S, Вы писали:

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


S_S>>>
S_S>>>//Тело этого метода будет изменено через SG, но он останется в assembly как мусор:
S_S>>>[MyGenerator] void MyMethod1_(){/*...*/}

S_S>>>//Генератор изменяет тело MyMethod1_, и добавляет его как новый метод, под тем же именем но без '_'
S_S>>>void MyMethod1(){/*...*/}
S_S>>>


S>>А partial void MyMethod1 без подчеркивания не надо объявлять?


S_S> Здесь partial не обязателен. Оно в студии так работает:

S_S> Как только к методу приписывается атрибут [MyGenerator], мгновенно без ручной перекомпиляции (в редакторе свой компилятор) появляется сгенерированный метод без подчеркивания(в автоматическом файле, который контролирует сама студия). Этот метод появляется и в intellisense и из кода на него можно сразу ссылаться. Работает прозрачно, для юзера как замена метода на лету. Если генератору не нужно тело метода, то придется атрибут ставить на partial заголовок метода без тела.

А можно ссылочку на [MyGenerator] void MyMethod1_(){/*...*/}
Что то не нашел.
и солнце б утром не вставало, когда бы не было меня
Re[9]: Новости C#12
От: Silver_S Ниоткуда  
Дата: 15.11.23 13:40
Оценка: 21 (1)
Здравствуйте, Serginio1, Вы писали:

S>А можно ссылочку на [MyGenerator] void MyMethod1_(){/*...*/}

S> Что то не нашел.

В сети должны быть получше примеры написания генераторов. Но если только чтобы посмотреть как студия работает с генераторами на готовом примере. Я когда-то переделал на скорую руку пример из какой-то статьи MS.
Тут корявый код генерации property: http://files.rsdn.org/7870/SourceGenerators.zip
Там нет бинарников, поэтому после первого Build для этого Solution надо перезапустить студию, чтобы редактор заметил генераторы.

Там есть пример применения к функции:

[Prop] void MyProp_(int val)
{
    _myProp = val;
}

//Генератор создает код:
int _myProp;
public int MyProp 
{
    get => this._myProp;
    set
    {
        if(value == this._myProp)
            return;
        MyProp_(value);               
    }
}
Re[3]: Новости C#12
От: IT Россия linq2db.com
Дата: 15.11.23 20:24
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Теперь и существующий код можно подменять!

S>https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md

А можно в подменённом коде вызывать подменяемый? Если да, то имеет смысл для всяко разных аспектов.
Если нам не помогут, то мы тоже никого не пощадим.
Re[4]: Новости C#12
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.11.23 22:36
Оценка:
Здравствуйте, IT, Вы писали:

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


S>> Теперь и существующий код можно подменять!

S>>https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md

IT>А можно в подменённом коде вызывать подменяемый? Если да, то имеет смысл для всяко разных аспектов.


Конечно, подмена делается просто по месту конкретного вызова в исходнике.
Правда должен быть именно вызов функции, а не конструктор.
Re[5]: Новости C#12
От: Разраб  
Дата: 15.11.23 23:53
Оценка:
Здравствуйте, gandjustas, Вы писали:

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


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


S>>> Теперь и существующий код можно подменять!

S>>>https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md

IT>>А можно в подменённом коде вызывать подменяемый? Если да, то имеет смысл для всяко разных аспектов.


G>Конечно, подмена делается просто по месту конкретного вызова в исходнике.

G>Правда должен быть именно вызов функции, а не конструктор.
Для методов есть DispatchProxy
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[6]: Новости C#12
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.11.23 07:02
Оценка:
Здравствуйте, Разраб, Вы писали:

IT>>>А можно в подменённом коде вызывать подменяемый? Если да, то имеет смысл для всяко разных аспектов.

G>>Конечно, подмена делается просто по месту конкретного вызова в исходнике.
G>>Правда должен быть именно вызов функции, а не конструктор.
Р>Для методов есть DispatchProxy
И что?
Re[4]: Новости C#12
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 16.11.23 07:41
Оценка:
Здравствуйте, IT, Вы писали:

S>> Теперь и существующий код можно подменять!

S>>https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md

IT>А можно в подменённом коде вызывать подменяемый? Если да, то имеет смысл для всяко разных аспектов.

Там подменяешь существующий вызов. Есть сторонняя сборка, и надо подменить вызов на свой в каких то случаях например через расширения.
Там пример
есть код
using System;
using System.Runtime.CompilerServices;

var c = new C();
c.InterceptableMethod(1); // (L1,C1): prints "interceptor 1"
c.InterceptableMethod(1); // (L2,C2): prints "other interceptor 1"
c.InterceptableMethod(2); // (L3,C3): prints "other interceptor 2"
c.InterceptableMethod(1); // prints "interceptable 1"

class C
{
    public void InterceptableMethod(int param)
    {
        Console.WriteLine($"interceptable {param}");
    }
}


И ты генеришь код который будет подменять вызов.
static class D
{
    [InterceptsLocation("Program.cs", line: /*L1*/, character: /*C1*/)] // refers to the call at (L1, C1)
    public static void InterceptorMethod(this C c, int param)
    {
        Console.WriteLine($"interceptor {param}");
    }

    [InterceptsLocation("Program.cs", line: /*L2*/, character: /*C2*/)] // refers to the call at (L2, C2)
    [InterceptsLocation("Program.cs", line: /*L3*/, character: /*C3*/)] // refers to the call at (L3, C3)
    public static void OtherInterceptorMethod(this C c, int param)
    {
        Console.WriteLine($"other interceptor {param}");
    }
}


Если
[InterceptsLocation("Program.cs", line: /*L2*/, character: /*C2*/)]

будет одинаковым у двух расширений, наверное будет ошибка
и солнце б утром не вставало, когда бы не было меня
Re[10]: Новости C#12
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 16.11.23 07:53
Оценка:
Здравствуйте, Silver_S, Вы писали:


S>>А можно ссылочку на [MyGenerator] void MyMethod1_(){/*...*/}

S>> Что то не нашел.

S_S>В сети должны быть получше примеры написания генераторов. Но если только чтобы посмотреть как студия работает с генераторами на готовом примере. Я когда-то переделал на скорую руку пример из какой-то статьи MS.

S_S>Тут корявый код генерации property: http://files.rsdn.org/7870/SourceGenerators.zip
S_S>Там нет бинарников, поэтому после первого Build для этого Solution надо перезапустить студию, чтобы редактор заметил генераторы.

Спасибо. Да уже давно можно обходиться без partial.
Просто генератор должен создать void MyMethod1 и он будет виден.
Как я написал здесь
http://rsdn.org/forum/dotnet/8636462.1
Автор: Serginio1
Дата: 16.11.23


interceptors нужны для подмены существующий вызов. Есть сторонняя сборка, и надо подменить вызов к методу в этой сборке на свой.
Благо есть доступ к непубличным методам не через рефлексию.
и солнце б утром не вставало, когда бы не было меня
Re[5]: Новости C#12
От: · Великобритания  
Дата: 16.11.23 10:02
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Там подменяешь существующий вызов. Есть сторонняя сборка, и надо подменить вызов на свой в каких то случаях например через расширения.

S> [InterceptsLocation("Program.cs", line: /*L1*/, character: /*C1*/)] // refers to the call at (L1, C1)
Всё равно не понимаю смысл. Если есть исходник — Program.cs — просто патчишь его как надо в нужных строках и перекомпилируешь.
А если исходника нет, то откуда брать номера строк — неясно.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[6]: Новости C#12
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 16.11.23 11:56
Оценка:
Здравствуйте, ·, Вы писали:

S>>Там подменяешь существующий вызов. Есть сторонняя сборка, и надо подменить вызов на свой в каких то случаях например через расширения.

S>> [InterceptsLocation("Program.cs", line: /*L1*/, character: /*C1*/)] // refers to the call at (L1, C1)
·>Всё равно не понимаю смысл. Если есть исходник — Program.cs — просто патчишь его как надо в нужных строках и перекомпилируешь.
·>А если исходника нет, то откуда брать номера строк — неясно.

Зачем тебе исходник? Ты заменяешь вызов в текущем коде!!! А к нему у тебя есть доступ
То есть вызывается не метод объекта, а расширения.

При этом расширение может брать данные из текущего класса. Компильнуть исходники, рефлексия, итд.
и солнце б утром не вставало, когда бы не было меня
Re[7]: Новости C#12
От: · Великобритания  
Дата: 16.11.23 12:28
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>>> [InterceptsLocation("Program.cs", line: /*L1*/, character: /*C1*/)] // refers to the call at (L1, C1)

S>·>Всё равно не понимаю смысл. Если есть исходник — Program.cs — просто патчишь его как надо в нужных строках и перекомпилируешь.
S>·>А если исходника нет, то откуда брать номера строк — неясно.
S>Зачем тебе исходник?
А как ещё узнать line и character?

S>Ты заменяешь вызов в текущем коде!!! А к нему у тебя есть доступ

S>То есть вызывается не метод объекта, а расширения.
S>При этом расширение может брать данные из текущего класса. Компильнуть исходники, рефлексия, итд.
Почему просто сам Program.cs отредактировать нельзя?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[6]: Новости C#12
От: Sinclair Россия https://github.com/evilguest/
Дата: 16.11.23 12:38
Оценка: +1
Здравствуйте, ·, Вы писали:

·>Всё равно не понимаю смысл. Если есть исходник — Program.cs — просто патчишь его как надо в нужных строках и перекомпилируешь.

Вы имеете в виду — "патчишь руками"?
Ну так там как раз смысл в том, чтобы патчить не руками, а автоматом.
То есть мат явно предлагается ставить в два хода:
1. Находим некие места в пользовательском коде, где мы могли бы "сделать лучше" — перенести часть вычислений из рантайма в компайл-тайм
2. Генерируем (при помощи SG) подходящий код-замену
3. Заменяем неэффективный вызов на эффективный.


Воображаемый пример. Вот, допустим, мы имеем вот такой вот код:
IEnumerable<int> Filter(IEnumerable<int> source)
  =>source.Where(n=>n%2==0);


При буквальном исполнении этот код соорудит объект-делегат, передаст его в Enumerable.Where, и будет выполнять косвенный вызов для каждого элемента коллекции source.
Но хитрый компайл-тайм код может сделать следующее:
1. Найти места вызовов Enumerable.Where(..., ...)
2. Сгенерировать для каждого из них уникальный метод:
public static class EnumerableHelper
{
  public static IEnumerable<int> Where2314234235569567(this IEnumerable<int> source, Func<int, bool> _)
  {
     foreach(var n in source)
       if(n=>n%2==0) 
         yield n;
  }
}

3. Заменить вызов в пользовательском коде:
IEnumerable<int> Filter(IEnumerable<int> source)
  => EnumerableHelper.Where2314234235569567(source, n=>n%2==0);



·>А если исходника нет, то откуда брать номера строк — неясно.

Номера строк берём из анализируемого пользовательского кода.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: Новости C#12
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.11.23 12:55
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Воображаемый пример. Вот, допустим, мы имеем вот такой вот код...

К сожалению для эффективного придется генерировать структуры-итераторы, но тогда сигнатуры не совпадут и интерсептор не сработает.

Хотя возможно сделают совместимость типов по приведению для подстановок в C#13
Re[8]: Новости C#12
От: Sinclair Россия https://github.com/evilguest/
Дата: 16.11.23 13:09
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>К сожалению для эффективного придется генерировать структуры-итераторы, но тогда сигнатуры не совпадут и интерсептор не сработает.

Там же и так структуры-итераторы. Единственное, что можно тут сделать более эффективно (локально) — это убрать конструирование ненужного делегата.
Но сама эта операция во-первых, O(1), во-вторых — дешёвая.
В третьих, есть шанс на то, что джит проинлайнит вызов Where2314234235569567, увидит, что делегат не используется, и выкинет его конструирование совсем.
Дойдут руки — поэкспериментирую.

G>Хотя возможно сделают совместимость типов по приведению для подстановок в C#13


Не успеваю за ходом мысли.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Новости C#12
От: Sinclair Россия https://github.com/evilguest/
Дата: 16.11.23 13:11
Оценка:
Здравствуйте, ·, Вы писали:
S>>При этом расширение может брать данные из текущего класса. Компильнуть исходники, рефлексия, итд.
·>Почему просто сам Program.cs отредактировать нельзя?
Можно, но неохота. Так-то можно вообще все оптимизации вручную сделать — но это раздувает код, и делает его сложнее в чтении и поддержке.
Хочется иметь прямо абстрактные абстракции, чтобы при изменении требований лёгким манием руки менять поведение программы.
Высокооптимизированный код — штука трудночитаемая, и плохоисправимая. Поэтому лучше, чтобы её порождал не человек руками, а неумолимая машина автоматом.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: Новости C#12
От: · Великобритания  
Дата: 16.11.23 13:14
Оценка: -1
Здравствуйте, Sinclair, Вы писали:

S>Вы имеете в виду — "патчишь руками"?

S>Ну так там как раз смысл в том, чтобы патчить не руками, а автоматом.
А какая разница?.. хоть руками, хоть "/usr/bin/patch" хоть, sed-скрипт или ещё чего. В любом случае почти неюзабельно. Если уж патчить, то байт-код. Его хотя бы анализировать гораздо проще.
Ведь нужен парсер синтаксиса и семантики C#, чтобы номера строк знать и т.п.

S>3. Заменить вызов в пользовательском коде:

Чем это принципиально отличается от "Взять текст Program.cs", "Сделать String.Replace()", "скомпилировать"?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[9]: Новости C#12
От: · Великобритания  
Дата: 16.11.23 13:25
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>·>Почему просто сам Program.cs отредактировать нельзя?

S>Можно, но неохота. Так-то можно вообще все оптимизации вручную сделать — но это раздувает код, и делает его сложнее в чтении и поддержке.
Проблема с оптимизациями в том, что они не универсальные. Машина задолбается искать места похожие на то, что можно оптимизировать и решать, стоит оптимизация того или нет и не сломается ли что. В итоге вместо того, чтобы просто отредактировать Program.cs ты будешь ипаться с машиной, чтобы заставить её отредактировать Program.cs как надо.
А если оптимизации относительно просты, то этим уже должен заниматься компилятор. Я вполне уверен, что "код соорудит объект-делегат... косвенный вызов" — давно под силу компилятору понять, что такое можно инлайнить.

S>Хочется иметь прямо абстрактные абстракции, чтобы при изменении требований лёгким манием руки менять поведение программы.

S>Высокооптимизированный код — штука трудночитаемая, и плохоисправимая. Поэтому лучше, чтобы её порождал не человек руками, а неумолимая машина автоматом.
Зачем машине работать с c# — языком для человеков, неясно.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.