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


139

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

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では、これが私が使用しているものです)。おそらく誰かが問題を明確にすることができるでしょう。ありがとう。


3
.NET 4の4つの引数引数については、Func<>最大16個の引数を受け入れます。
アンソニーペグラム

説明をありがとう。.NET 3.5を使用しています。
Marlon、

9
コンパイラにそれをaだと思わせるのはなぜFunc<string, bool>ですか?それはConverter<string, bool>私には見えます!
Ben Voigt、2011


3
VBを見逃すこともありますDim comparer = Function(value$) value <> "0"
。– Slai

回答:


155

他の人たちはすでに、あなた意味する可能性があるデリゲート型の可能性は無限にあると指摘しています。何がそんなに特別なものFunc、それはデフォルトの代わりにされるに値することPredicateActionまたは任意の他の可能性は?そして、ラムダの場合、式ツリー形式ではなくデリゲート形式を選択することが明白なのはなぜですか?

しかしFunc、それは特別であり、ラムダまたは匿名メソッドの推論された型は何かのFuncであると言えます。まだいろいろな問題があります。次の場合、どのタイプを推測しますか?

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

Func<T>何も参照をとるタイプはありません。

var x2 = y=>123;

戻り値はわかりますが、仮パラメーターのタイプはわかりません。(それとも私たちですか?戻り値はint?long?short?byte?)

var x3 = (int y)=>null;

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

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

繰り返しになりますが、戻り値の型はわかりません。今回無効になる可能性があります。

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

これは、voidを返すステートメントラムダ、またはqに割り当てられた値を返す何かを意図したものですか?どちらも合法です。どれを選べばいいの?

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

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

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型が何であるかを解明します。


43
SOの回答をいつ本にまとめるのですか?私はそれを購入します:)
Matt Greer

13
私は、SOの回答のエリックリッペルト本の提案の2番目です。推奨タイトル:「スタックからの反射」
Adam Rackis

24
@エリック:良い答えですが、これは実際にはDで完全に正常に機能するため、これを不可能であると説明するのは少し誤解を招く可能性があります。デリゲートリテラルに独自のタイプを指定せず、代わりに依存させただけです。彼らの文脈で...だから私見の答えは何よりも「それが私たちがそれを作った方法だから」です。:)
user541686

5
@abstractdissonanceまた、コンパイラはオープンソースであることに注意します。この機能に関心がある場合は、それを実現するために必要な時間と労力を寄付することができます。プルリクエストを提出することをお勧めします。
Eric Lippert

7
@AbstractDissonance:開発者と時間という限られたリソースの観点からコストを測定しました。この責任は神によって与えられませんでした。開発部門の副社長から課せられた。C#チームが予算プロセスをどうにかして無視できるという考えは奇妙なものです。確かに、C#コミュニティに要望、Microsoftの戦略的使命、設計における独自の優れた趣味を念頭に置いた専門家を注意深く慎重に検討することで、トレードオフはありました。
Eric Lippert 2017年

29

確かに知っているのはEric Lippertだけですが、それはデリゲート型のシグネチャが型を一意に決定しないためだと思います。

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

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

以下は、2つの考えられる推論ですvar

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

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


1
マイクロソフトの他の多くの人も確かに知っていると思います。;)しかし、はい、あなたは主な理由をほのめかします、何もないのでコンパイル時の型は決定できません。言語仕様のセクション8.5.1は、暗黙的に型付けされた変数宣言で匿名関数を使用できないようにするこの理由を具体的に強調しています。
アンソニーペグラム

3
うん。さらに悪いことに、ラムダの場合、デリゲート型になるかどうかさえわかりません。それは式ツリーかもしれません。
Eric Lippert、2011

興味のある人のために、私はこれについてもう少しアップ書いて、どのようにC#とF#は対照的にアプローチmindscapehq.com/blog/index.php/2011/02/23/...
itowlson

コンパイラがラムダ関数に対してC ++が行うような新しい一意の型を単に作成できないのはなぜ
ですか

「.NETタイプシステムのレベルで」どう違うのですか?
arao6

6

エリックリッパートは彼が言うところについてそれについての古い投稿があります

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


これは、言語仕様のセクション8.5.1で強調されています。暗黙的に型指定されたローカル変数に使用するには、「初期化子式はコンパイル時の型でなければなりません」。
Anthony Pegram

5

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

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

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


2

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

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

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

無名メソッドに関して以下を検討します。

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

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

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


1

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

「どのように不明瞭なタイプをタイプする必要がないようにするの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、パラメーターとして事前定義が必要なメソッドを呼び出さないことを前提としています。その場合、不明瞭な文字列を入力する必要があります:/


0

それはどうですか?

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);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.