web-dev-qa-db-ja.com

匿名メソッドをvarに割り当てることができないのはなぜですか?

私は次のコードを持っています:

Func<string, bool> comparer = delegate(string value) {
    return value != "0";
};

ただし、次はコンパイルされません。

var comparer = delegate(string value) {
    return value != "0";
};

なぜコンパイラーはそれがFunc<string, bool>?文字列パラメーターを1つ受け取り、ブール値を返します。代わりに、エラーが表示されます。

暗黙的に型指定されたローカル変数に匿名メソッドを割り当てることはできません。

私は1つの推測がありますが、それはvarバージョンがコンパイルされている場合であり、次のものがあると一貫性が失われます:

var comparer = delegate(string arg1, string arg2, string arg3, string arg4, string arg5) {
    return false;
};

Func <>は最大4つの引数しか使用できないため、上記は意味がありません(.NET 3.5では、これが使用されています)。おそらく誰かが問題を明確にすることができます。ありがとう。

132
Marlon

他の人たちは、あなたがcouldが意味する可能性がある無限の可能なデリゲートタイプがあることをすでに指摘しています。 FuncPredicateの代わりにデフォルトになるに値するほどActionの特別な点は何ですか?また、ラムダの場合、式ツリー形式ではなくデリゲート形式を選択することが意図されていることが明らかなのはなぜですか?

しかし、Funcは特別であり、ラムダまたは匿名メソッドの推定型は何かのFuncであると言えます。まだあらゆる種類の問題があります。次の場合にどのタイプを推測しますか?

var x1 = (ref int y)=>123;

Refを取得するFunc<T>タイプはありません。

var x2 = y=>123;

戻り値はわかりますが、仮パラメーターの型はわかりません。 (または、戻り値はint?long?short?byte?)

var x3 = (int y)=>null;

戻り値の型はわかりませんが、無効にすることはできません。戻り値の型は、任意の参照型またはNULL値を許可する値型です。

var x4 = (int y)=>{ throw new Exception(); }

繰り返しますが、戻り値の型はわかりませんが、今回はvoidにすることができます。

var x5 = (int y)=> q += y;

それは、voidを返すステートメントlambdaか、qに割り当てられた値を返す何かを意図したものですか?両方とも合法です。どちらを選ぶべきですか?

さて、あなたは、それらの機能のいずれもサポートしていないと言うかもしれません。タイプをうまく処理できる「通常の」ケースをサポートしてください。それは役に立ちません。どうすれば私の生活が楽になりますか?機能が時々動作し、時々失敗する場合、それらの失敗状況のすべてにdetectにコードを記述し、それぞれの意味のあるエラーメッセージ。それでも、すべての動作を指定し、文書化し、テストを作成する必要があります。これは、非常に高価な機能であり、ユーザーのキーストロークを半ダース節約できます。半分の時間で機能せず、機能する場合にほとんどメリットをもたらさない機能のテストケースを書くのに多くの時間を費やすよりも、言語に価値を付加するより良い方法があります。

実際に役立つ状況は次のとおりです。

var xAnon = (int y)=>new { Y = y };

なぜなら、そのことには「話せる」タイプがないからです。しかし、私たちは常にこの問題を抱えており、メソッドの型推論を使用して型を推測しています。

Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; }
...
var xAnon = WorkItOut((int y)=>new { Y = y });

そして今、メソッド型推論はfunc型が何であるかを決定します。

149
Eric Lippert

Eric Lippertだけが確実に知っていますが、デリゲート型の署名が型を一意に決定しないためだと思います。

あなたの例を考えてみましょう:

var comparer = delegate(string value) { return value != "0"; };

varがどうあるべきかについて、2つの可能な推論があります。

Predicate<string> comparer  = delegate(string value) { return value != "0"; };  // okay
Func<string, bool> comparer = delegate(string value) { return value != "0"; };  // also okay

コンパイラはどちらを推測すべきですか?どちらかを選択する正当な理由はありません。また、Predicate<T>Func<T, bool>と機能的に同等ですが、.NETタイプシステムのレベルでは依然として異なるタイプです。したがって、コンパイラはデリゲート型を明確に解決できず、型推論に失敗する必要があります。

29
itowlson

Eric Lippertには古い post があり、彼の言うところ

実際、C#2.0仕様ではこれを呼び出しています。メソッドグループ式と匿名メソッド式は、C#2.0では型のない式であり、ラムダ式はC#3.0ではそれらを結合します。したがって、暗黙の宣言の右側に「裸」に見えることは違法です。

6
Brian Rasmussen

異なるデリゲートは異なるタイプと見なされます。たとえば、ActionMethodInvokerとは異なり、ActionのインスタンスをMethodInvoker型の変数に割り当てることはできません。

したがって、() => {}のような匿名デリゲート(またはラムダ)が与えられた場合、それはActionまたはMethodInvokerですか?コンパイラーは判断できません。

同様に、string引数を取り、boolを返すデリゲート型を宣言した場合、コンパイラは、デリゲート型の代わりにFunc<string, bool>が本当に必要であることをどのように知るのでしょうか。デリゲートタイプを推測することはできません。

5
Stephen Cleary

次のポイントは、暗黙的に型指定されたローカル変数に関するMSDNからのものです。

  1. varは、ローカル変数が同じステートメントで宣言および初期化されている場合にのみ使用できます。変数をnull、メソッドグループ、または匿名関数に初期化することはできません。
  2. Varキーワードは、初期化ステートメントの右側の式から変数のタイプを推測するようコンパイラーに指示します。
  3. Varキーワードは「バリアント」を意味するものではなく、変数が緩やかに型付けされていること、または遅延バインドされていることを示すものではないことを理解することが重要です。これは、コンパイラが最も適切な型を決定して割り当てることを意味します。

MSDNリファレンス:暗黙的に型指定されたローカル変数

匿名メソッドに関する次のことを考慮してください。

  1. 匿名メソッドを使用すると、パラメーターリストを省略できます。

MSDNリファレンス:匿名メソッド

無名メソッドは実際には異なるメソッドシグネチャを持っている可能性があるため、コンパイラは割り当てるのに最も適切なタイプが何であるかを適切に推測することはできません。

2
nybbler

私の投稿は実際の質問には答えませんが、以下の根本的な質問には答えます。

Func<string, string, int, CustomInputType, bool, ReturnType>のような不気味なタイプを入力しなくて済むようにするにはどうすればよいですか?」 [1]

私は怠け者/ハッカーのプログラマーなので、Func<dynamic, object>を使用して実験しました。これは、単一の入力パラメーターを受け取り、オブジェクトを返します。

複数の引数の場合、次のように使用できます。

dynamic myParams = new ExpandoObject();
myParams.arg0 = "whatever";
myParams.arg1 = 3;
Func<dynamic, object> y = (dynObj) =>
{
    return dynObj.arg0.ToUpper() + (dynObj.arg1 * 45); //screw type casting, amirite?
};
Console.WriteLine(y(myParams));

ヒント:オブジェクトを返す必要がない場合は、Action<dynamic>を使用できます。

ええ、おそらくあなたのプログラミングの原則に反することを知っていますが、これは私にとって理にかなっており、おそらくいくつかのPythonコーダー。

私はデリゲートの初心者です...私が学んだことを共有したかっただけです。


[1] これは、パラメーターとして事前定義されたFuncを必要とするメソッドを呼び出さないことを前提としています。その場合、そのひどい文字列を入力する必要があります:/

1
Ambrose Leung

それはどうですか?

var item = new
    {
        toolisn = 100,
        LangId = "ENG",
        toolPath = (Func<int, string, string>) delegate(int toolisn, string LangId)
        {
              var path = "/Content/Tool_" + toolisn + "_" + LangId + "/story.html";
              return File.Exists(Server.MapPath(path)) ? "<a style=\"vertical-align:super\" href=\"" + path + "\" target=\"_blank\">execute example</a> " : "";
        }
};

string result = item.toolPath(item.toolisn, item.LangId);
0
mmm