ufcpp-live / UfcppLiveAgenda

@ufcpp live streaming agenda
MIT License
24 stars 2 forks source link

.NET 8 Preview 6 & Visual Studio 17.7 Preview 3 #74

Closed ufcpp closed 11 months ago

ufcpp commented 1 year ago

New C# 12 preview features:

.NET SDK の API diff (core 8617)見るに

IDE は拡張マネージャーの UI 新しくなったくらい?

ufcpp commented 1 year ago

Inline Arrays

using System.Runtime.CompilerServices;

Buffer<int> b = default;

// Span への変換は暗黙。
Span<int> s = b;

// Length 取るのは Span 介さないと無理そう。
Console.WriteLine(s.Length);

// インデクサーはコンパイラー提供。
b[1] = 1;

// foreach もコンパイラー提供。
foreach (var x in b)
{
    Console.WriteLine(x);
}

// 今、属性でサイズ指定する。
// Buffer<T, 10> とか書きたいけど、ジェネリック整数引数が .NET にはないんで。
// 利用側じゃなくて型定義側に属性つける。サイズごとに別の型作る必要あり。
[InlineArray(10)]
public struct Buffer<T>
{
    private T _element0;
    // ↑ 今、 private mamber unused サジェストが出っぱなしになったりする。
}
ufcpp commented 1 year ago

UnsafeAccessor

using System.Runtime.CompilerServices;

var p = new Private();
GetValue(p) = 99;
Console.WriteLine(p); // 99

// アクセス制限無視してメンバーにアクセスする手段を用意したらしい。
// 今まではリフレクション(IL Emit)でやりたい放題やってたけども、
// AOT (リフレクションをソースジェネレーターで置き換えたい)で困るからって。
//
// ランタイム側で UnsafeAccessor 属性付きの external メソッドを特別扱い(intrinsic)してる。
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_value")]
extern static ref int GetValue(Private x);

class Private
{
    private int _value; // Make field readonly 出っぱなしにはなる。

    public override string ToString() => _value.ToString();
}
ufcpp commented 1 year ago

ConfigureAwaitOptions

var cts = new CancellationTokenSource();
cts.Cancel();

try
{
    await Task.Delay(100, cts.Token);
}
catch (OperationCanceledException)
{
    // キャンセルで例外いちいち出さないでほしくて無条件 catch からの無視とかよくやる。
}

// だったらさ最初から throw しないでくれと。
await Task.Delay(100, cts.Token)
    .ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);

// 他にもパフォーマンス制御用の enum 値がいくつか。
ufcpp commented 1 year ago

Interceptor 動くコード書けた。

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}");
    }
}

// generated code
static class D
{
    [InterceptsLocation(@"[full path to Program]\Program.cs", 4, 3) ]
    public static void InterceptorMethod(this C c, int param)
    {
        Console.WriteLine($"interceptor {param}");
    }

    [InterceptsLocation(@"[full path to Program]\Program.cs", 5, 3)]
    [InterceptsLocation(@"[full path to Program]\Program.cs", 6, 3)]
    public static void OtherInterceptorMethod(this C c, int param)
    {
        Console.WriteLine($"other interceptor {param}");
    }
}

<Features>InterceptorsPreview</Features> オプション必須。

属性は自前でも OK。

namespace System.Runtime.CompilerServices;

[Diagnostics.Conditional("CompileTimeOnly")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
internal sealed class InterceptsLocationAttribute : Attribute
{
    public InterceptsLocationAttribute(string path, int lineNumber, int columnNumber)
    {
        _ = path;
        _ = lineNumber;
        _ = columnNumber;
    }
}

ドキュメントから属性のシグネチャとか、ファイル名がフルパスでないとダメとかの変更があり。 「intercept される側」には属性不要になったっぽい。

Feature オプションの指定が必要だったり、他の機能となんか実装者のノリの違いあり。 中の人曰く、「language feature じゃなくて compiler feature とする」とのこと。 (言語仕様であれば、「オプションで単機能の on/off とかやりたくない」ってずっと言ってる。NRT が例外中の例外。) (Obsolete 属性とか Conditional 属性とかも「言語仕様」というよりは「コンパイラーが属性を特別扱いする」なので、それと似たノリ。)

ufcpp commented 1 year ago

pathmap、コンパイラーは対応してるけど、IDE は対応してなさそう。

    <ProjRoot>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)'))</ProjRoot>
    <PathMap>$(ProjRoot)=.</PathMap>
    [InterceptsLocation(@".\Program.cs", 4, 3) ]
    public static void InterceptorMethod(this C c, int param)
    {
        Console.WriteLine($"interceptor {param}");
    }

ビルドは通るけど、Visual Studio のエディター内では赤線出っぱなし。

ufcpp commented 1 year ago

F12 で intercept してる側に飛べない。

ufcpp commented 1 year ago

ref InlineArray ダメそう。

using System.Runtime.CompilerServices;

// size 見るに 80 なのでちゃんと InlineArray 効いてそう。
unsafe
{
    Console.WriteLine(sizeof(S));
}

// new はできる。
var s = new S();

int x = 1;

// これがなんかダメっぽい。
// CS0021 Cannot apply indexing with [] to an expression of type 'S'
s[0] = ref x;

[InlineArray(10)]
ref struct S
{
    ref int x;
}
using System.Runtime.CompilerServices;

int x = 1;
scoped RefInt r = new() { Ref = ref x };

// この場合エラー内容が変わった。
// CS0306 The type 'RefInt' may not be used as a type argument
// 内部的に Span<T> を使ってるから、ref struct を型引数にできない系のエラーっぽい。
scoped var s = new S();
s[0] = r;

[InlineArray(10)]
ref struct S
{
    RefInt x;
}

ref struct RefInt
{
    public ref int Ref;
}
ufcpp commented 1 year ago

interceptor、 file-local な型の拡張メソッドでも動く。

top-level statements 内に書いた local function に対しても働く。

m("abc");

int x = 1;
n();

static void m(string s) { }

void n() { x++; }
Console.WriteLine(x);
    [InterceptsLocation(@"C:\src\ConsoleApp1\ConsoleApp1\Program.cs", 7, 1)]
    internal static void M(string param)
    {
        Console.WriteLine($"top-level static local function {param}");
    }

    [InterceptsLocation(@"C:\src\ConsoleApp1\ConsoleApp1\Program.cs", 10, 1)]
    internal static void M()
    {
        Console.WriteLine($"top-level non-static local function");
    }
ufcpp-live commented 1 year ago

報告事案かも

using System.Runtime.CompilerServices;

var buffer = new Buffer();

// ユーザー定義 Enumerator 呼ばれないっぽい。
// 意図的?仕様漏れ?

foreach (var x in buffer)
{
    Console.WriteLine(x);
}

[InlineArray(10)]
struct Buffer
{
    private int _firstElement;

    public Enumerator GetEnumerator() => default;

    public struct Enumerator
    {
        public int Current => 0;
        public bool MoveNext() => false;
    }
}
ufcpp-live commented 1 year ago
using System.Runtime.CompilerServices;

var buffer = new Buffer();

int x = 1;
buffer[0] = ref x; // error on this line

// no error on the type.
[InlineArray(10)]
ref struct Buffer
{
    private ref int _firstElement;
}
ufcpp-live commented 1 year ago
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

var buffer = new Buffer();

// ユーザー定義インデクサー呼ばれないっぽい。
// 意図的?仕様漏れ?

buffer[1] = 123;

foreach (var x in buffer)
{
    Console.WriteLine(x);
}

[InlineArray(10)]
struct Buffer
{
    private int _firstElement;

    [UnscopedRef]
    public ref int this[int index] => ref _firstElement;
}
ufcpp-live commented 1 year ago

拡張メソッドいけた。

static class Ex
{
    [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_value")]
    public extern static ref int GetValue(this Private x);
}
ufcpp-live commented 1 year ago
// こいつに interceptor 挟んでくれればいいんじゃない?
Regex.Match("", "");

partial class C
{
    // いちいち、1正規表現 1メソッド書くのやだーーー
    [GeneratedRegex("")]
    public static partial Regex M();
}