Closed ufcpp closed 2 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 への代入」を使ってる。
通常のラムダ式の型解決の方が優先されるのでオーバーロードがいっぱいある場合、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");
}
オーバーロード解決の闇
// 他の引数も絡むと 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");
}
以下の 「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"); }
}
デリゲートのページには https://ufcpp.net/study/csharp/sp_delegate.html#csharp10 だけ足した。 詳細は https://ufcpp.net/study/csharp/functional/fun_localfunctions/ の方に書く。
https://ufcpp.net/study/csharp/sp_delegate.html#natural-type
「後付けだからちょっとオーバーロード解決のルールが変」みたいな話もなくはないんだけど、うちのサイトには書かなくてもいいか…
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
Delegate
とかvar
とかに代入可能にnew Func<int, int>(x => x * x)
みたいな型の明示も不要書く場所: ~~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 の方にも要る。