14

あまり難解なことをしているとは思いませんが、これについて他に質問はありません。

次のコード(私はそれを本質に還元しました)はC#4でコンパイラエラーを生成します。ただし、type引数が何であるかは明らかです-最大公約数(「クラスA」)も明示的に定義されていますメソッド「Frob」の戻り型。コンパイラーはラムダ式のすべての戻り型のリストを作成し、それらの共通の祖先を見つけるために祖先ツリーを作成し、それを含むメソッドの期待される戻り型と調整するべきではありませんか?

メソッド'System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable、System.Func)'の型引数は、使用法から推測できません。タイプ引数を明示的に指定してみてください。

using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace Sample
{
    public abstract class A
    {
        private A(int index) { /* ... */ }

        public sealed class A1 : A
        {
            public A1(string text, int index)
                : base(index)
            { /* ... */ }
        }

        public sealed class A2 : A
        {
            public A2(int index)
                : base(index)
            { /* ... */ }
        }

        private static Regex _regex = new Regex(@"(to be)|(not to be)");
        public static IEnumerable<A> Frob(string frobbable)
        {
            return _regex.Matches(frobbable)
                .Cast<Match>()
                .Select((match, i) =>
                {
                    if (match.Groups[1].Success)
                    {
                        return new A1(match.Groups[1].Value, i);
                    }
                    else
                    {
                        return new A2(i);
                    }
                });
        }
    }
}
4

2 に答える 2

21

これは、C#4仕様のセクション7.5.2.12です。

匿名関数Fの推論された戻り型は、型推論および過負荷解決中に使用されます。推論された戻り型は、すべてのパラメーター型が既知である無名関数に対してのみ決定できます。これは、それらが明示的に指定されているか、無名関数変換によって提供されるか、または囲んでいるジェネリックメソッド呼び出しの型推論中に推論されるためです。推定リターンタイプは次のように決定されます。

  • Fの本体が式の場合、推定されるFの戻り型はその式の型です。
  • Fの本体がブロックであり、ブロックのreturnステートメントの式のセットが最も一般的なタイプT(§7.5.2.14)である場合、Fの推定されるreturnタイプはTです。
  • そうしないと、Eのリターンタイプを推測できません。

セクション7.5.2.14はこれです:

場合によっては、一連の式に対して共通の型を推測する必要があります。特に、暗黙的に型指定された配列の要素型とブロック本体を持つ無名関数の戻り型は、この方法で検出されます。

直感的には、一連の式E1…Emが与えられた場合、この推論はメソッドを呼び出すことと同等であるはずです。

Tr M<X>(X x1 … X xm)

Eiを引数として。

より正確には、推論は固定されていない型変数Xから始まります。次に、出力型推論が各EiからXに対して行われます。最後に、Xが固定され、成功した場合、結果の型Sが式の結果として得られる最も一般的な型になります。そのようなSが存在しない場合、式には最適な共通型がありません。

したがって、次のようになります。

void M<X>(X x1, X x2) {}

A1 a1 = new A1();
A2 a2 = new A2();
M(a1, a2);

...これは、の型引数の決定に失敗するためX、戻り値の推論も同じように失敗します。

どちらかの戻り値をにキャストすると、うまくいくと思いますA

于 2012-07-20T22:14:41.127 に答える
8

これを指示する特定のC#仕様句がどこかにあると思います。(編集:ジョン・スキートはそれを見つけて彼の答えに投稿しました)

通常、このようなラムダ(または三項演算など)は、あいまいさを避けるために、各ステージで同じ正確な戻り型を持つ必要があります。たとえば、あなたの場合、タイプを返したいですかA、それともObject?インターフェイスまたは複数レベルの継承をミックスに投入すると、さらに楽しくなります。

この場合の最善の策は、各returnステートメントをキャストAして、一時変数に入力または格納することです。

if (match.Groups[1].Success)
    return (A)(new A1(match.Groups[1].Value, i));
else
    return (A)(new A2(i));

また

A returnValue;

if (match.Groups[1].Success)
    returnValue = new A1(match.Groups[1].Value, i);
else
    returnValue = new A2(i);

return returnValue;

Select編集:推測された型がなくても大丈夫な場合は、次のコマンドでクエリを明示的に呼び出すことができます。

.Cast<Match>()
.Select<Match, A>((match, i) =>
{
    if (match.Groups[1].Success)
        return new A1(match.Groups[1].Value, i);
    else
        return new A2(i);
});

次に、コンパイラは、戻り型が暗黙的に互換性があることを確認しますA(それらは)

于 2012-07-20T22:07:55.693 に答える