ufcpp / UfcppSample

http://ufcpp.net/ 向けのサンプル
Apache License 2.0
134 stars 40 forks source link

Lambda improvements #359

Closed ufcpp closed 2 years ago

ufcpp commented 3 years ago

https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-attributes.md https://ufcpp.net/blog/2021/3/lambdaimprovements/ https://ufcpp.net/blog/2021/8/net6p7/#lambda

書く場所: ~~https://ufcpp.net/study/csharp/sp_delegate.html がいいか、 https://ufcpp.net/study/csharp/functional/fun_localfunctions/ がいいか。~~

https://ufcpp.net/study/csharp/functional/fun_localfunctions/ の末尾に「C# 10.0 のラムダ式」って節を作ろうかと。

自然な型はラムダ式だけじゃなくてメソッドグループにも効くはず。 これだけは sp_delegate の方にも要る。

ufcpp commented 3 years ago

https://github.com/ufcpp/UfcppSample/issues/345#issuecomment-898338373 とも関連。 一番の用途は ASP.NET の「minimal API テンプレート」。 .NET 6 から、デフォルトの Web API テンプレート(dotnet new web コマンドで生成)が以下の通り簡素化する。

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.MapGet("/", () => "Hello World!");

app.Run();

これの add.MapGet のところで「Delegate への代入」を使ってる。

ufcpp commented 3 years ago

通常のラムダ式の型解決の方が優先されるのでオーバーロードがいっぱいある場合、Delegate に来るのは最後の最後。

// 引数・戻り値なし。
A.M(() => { });

// 引数あり。
A.M((int x) => { });    // 明示的に Action<int>
A.M(x => { });          // 推論で Action<int>
A.M((string x) => { }); // Action<T>

// 戻り値あり。
A.M(() => 0);       // 明示的に Func<int>
A.M(() => default); // 推論で Func<int>
A.M(() => "");      // Func<T>

// Delegate に流れるやつ。
A.M(int (ref int x) => x);
A.M(int (int x) => x);

class A
{
    // 引数・戻り値なし。
    public static void M(Action _) => Console.WriteLine("Action");

    // 引数あり。
    // int だけ具象型のを用意。残りは generic。
    public static void M(Action<int> _) => Console.WriteLine("Action<int>");
    public static void M<T>(Action<T> _) => Console.WriteLine("Action<T>");

    // 戻り値あり。
    // int だけ具象型のを用意。残りは generic。
    public static void M(Func<int> _) => Console.WriteLine("Func<int>");
    public static void M<T>(Func<T> _) => Console.WriteLine("Func<T>");

    // その他なんでも受け取れるやつ。
    // C# 10.0 新機能の lambda improvements の1つで、
    // 「可能なら Action か Func、それがダメなら匿名のデリゲート型を作って渡す」仕様(delegate natural type)が入ってる。
    public static void M(Delegate _) => Console.WriteLine("Delegate");
}
ufcpp commented 3 years ago

オーバーロード解決の闇

// 他の引数も絡むと Delegate へのラムダ式代入働かないことがあるっぽい。

// これが解決できないみたい。
// ニッチな需要だし、既存 API が破壊的変更になりかねないのでそうまでして解決したいとは思わないとのことで「仕様」に。
A.M("", () => { });

// これならOK。
A.M((object)"", () => { });
// これでもOK。
A.M("", (Delegate)(() => { }));

class A
{
    public static void M(object x, Action y) => Console.WriteLine("object, Action");
    public static void M(string x, Delegate y) => Console.WriteLine("string, Delegate");
}
ufcpp commented 3 years ago

以下の 「Extensions or error?」 の行、エラーにすることにしたみたい。 これ(拡張メソッドで具体的な型指定)はそこそこ需要ありそうだけど、解決するのはコンパイラーが複雑になりすぎるとのこと。

using static System.Console;

var w = new Widget();
w.Invoke((int arg) => { }, 1); // Widget
w.Invoke(arg => { }, 2);       // Extensions or error?

class Widget
{
    public void Invoke(Delegate d, params object[] args) { WriteLine("Widget"); }
}

static class Extensions
{
    public static void Invoke<T>(this Widget w, Action<T> action, T arg) { WriteLine("Extensions"); }
}
ufcpp commented 2 years ago

デリゲートのページには https://ufcpp.net/study/csharp/sp_delegate.html#csharp10 だけ足した。 詳細は https://ufcpp.net/study/csharp/functional/fun_localfunctions/ の方に書く。

ufcpp commented 2 years ago

https://ufcpp.net/study/csharp/sp_delegate.html#natural-type

「後付けだからちょっとオーバーロード解決のルールが変」みたいな話もなくはないんだけど、うちのサイトには書かなくてもいいか…

ufcpp commented 2 years ago

https://ufcpp.net/study/csharp/cheatsheet/ap_ver10/#lambda-improvement