2017-01-02 9 views
3

コンパイラがメソッドSetAll(PropertyInfo, int)を期待しているので、次の呼び出しは失敗します。次の呼び出しがなぜあいまいですか?

var infos = GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); 

var setters = infos.Select(SetAll); // no overload matches delegate. 

private Action SetAll(PropertyInfo info, object obj) =>() => info.SetValue(this, obj); 

したがって、コンパイラはこのオーバーロードを使用できません。 intobjectにキャストできません。

これを念頭に置いて、なぜ次の呼び出しがあいまいですか?

var b = infos.Select(SetAll); // ambiguous between Select<PropertyInfo, int, Action> 
           //    and Select<PropertyInfo, Action> 

private Action SetAll(PropertyInfo info, object obj) =>() => info.SetValue(this, obj); 

private Action SetAll(PropertyInfo info) =>() => info.SetValue(this, null); 

コンパイラがオブジェクトに何らかの方法でオーバーロードを使用できない場合、なぜここで苦労しているのですか?


ここには実際のコードがあります。私はこの問題に簡単に対処することができますが、ちょうど骨董品です。

var infos = GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); 

if (useDefaultsOnReset) 
{ 
    var defaults = infos.Select(GetAll); 
    _resetters = infos.Zip(defaults, SetAll).ToArray(); 
} 
else 
{ 
    _resetters = infos.Select(SetAll).ToArray(); // error 
} 

private object GetAll(PropertyInfo info) => info.GetValue(this); 
private Action SetAll(PropertyInfo info, object obj) =>() => info.SetValue(this, obj); 
private Action SetAll(PropertyInfo info) =>() => info.SetValue(this, null); 
+1

実際、[mcve]はここであなたを手伝ってくれます。 –

+0

'(x、i)=> SetAll(x、i)'は動作します。それはそれに関連していますか? –

+0

よくオブジェクトを使わずにオーバーロードを使いたいので、 '(x)=> SetAll(x)'になりますが、コンパイラはそれを理解できません。メソッドグループを使いたいのですが...:P @PatrickHofman –

答えて

3

これは、System.Func<in T1, in T2, out TResult>がその引数型で反則であるためです。これは対応する型パラメータのin修飾子で示されます。これは、タイプT1または任意のタイプT1を割り当てることができる関数に一致し、タイプT2または任意のタイプT2のアサインを割り当てることができることを意味します。最初の署名は、インデックスを組み込んでいないEnumerable.Selectのオーバーロードと一致します。ただし、にはintが割り当てられるため、2番目の署名は実際にはEnumerable.Selectのオーバーロードと一致します。

これを実証する。単に任意のクラスを作成し、そうするようにプログラムを変更するだけです。

private Action SetAll(PropertyInfo info, A a) =>() => info.SetValue(this, obj); 
private Action SetAll(PropertyInfo info) =>() => info.SetValue(this, null); 

class A {} 

あなたはintAに割り当てないようにエラーが消え遵守します。

コメントで説明したように、私は考慮に入れなかったしわがあります。反変関係が参照型の間、次の両方誤差で

Func<int, Action> f; 

Func<object, Action> g; 
を考える 直接 intを取るデリゲート間割り当てる際に値型に制限されないが、それは具体的に動作しないジェネリック間と object成立します

g = f; 
f = g; 

我々がintを交換する場合は、いくつかのクラスがA

言います
Func<A, Action> f; 

Func<object, Action> g; 

オブジェクトはAではないので、最初はエラーですが、2番目の方法は上記のように成功します。

g = f; 
f = g; 
+1

興味深いことに共変は 'Func 'から 'Func 'に作用しますが、反変は働きません。 'Func 'を 'Func 'にキャストできないのですか?あなたが言ったことは、私にはちょっと意味があります。 (それでも完全には得られませんでした)私はそのコンパイラのバグだと思っていましたが、明らかにそうではありません –

+0

2番目の割り当てを実行できないのは、 'int'が' object'を渡す必要がある関数です。これを可能にするDartのような言語がありますが、厳密には不健全であると考えられています。その動作をバイバリアンスと呼びます。 –

+0

実際、私は誤解していると思います。変数を使用した例による回答の更新 –

0

メソッドグループを操作するには、ダミー変数を設定する必要があります。

private Action SetAll(PropertyInfo info, object obj) =>() => info.SetValue(this, obj); 
private Action SetAll(PropertyInfo info, int _) =>() => info.SetValue(this, null); 
//           ^this is dummy but lets you use methodgroup 

次にこれは、インデクサとSelectを使用します

infos.Select(SetAll).ToArray(); 

に動作しますが、それはあまり重要はずの。

関連する問題