为什么跟随电话不明确?

时间:2017-01-02 15:27:49

标签: c# .net ambiguous-call

以下调用将失败,因为编译器需要方法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);

这意味着编译器无法以任何方式使用此过载。它无法将int投射到object

考虑到这一点,为什么下面的电话不明确?

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);

2 个答案:

答案 0 :(得分:3)

这是因为System.Func<in T1, in T2, out TResult>在其参数类型中是逆变的。这由相应类型参数上的in修饰符表示。这意味着它是任何带有类型T1或任何类型T1的参数的函数的匹配,以及T2类型的参数或任何类型{{ 1}}可以分配给。您的第一个签名匹配未包含索引的T2重载。但是,您的第二个签名实际上与Enumerable.Select的重载相匹配,因为Enumerable.Select可以分配给int,因此会合并索引。

为了证明这一点。只需创建一个任意类并更改您的程序。

object

您将观察到错误消失,因为private Action SetAll(PropertyInfo info, A a) => () => info.SetValue(this, obj); private Action SetAll(PropertyInfo info) => () => info.SetValue(this, null); class A {} 无法分配给int

正如评论中所讨论的那样,我没有考虑到皱纹。逆向关系在引用类型之间以及不限制为值类型的泛型之间保持不变,但是在直接在使用Aint的代理之间进行分配时,它们不起作用 给定

object

以下是两个错误

Func<int, Action> f;

Func<object, Action> g;

但是,如果我们将g = f; f = g; 替换为某个类int

A

第一个是错误,因为对象不是A,但第二个成功,如上所述。

Func<A, Action> f;

Func<object, Action> g;

答案 1 :(得分: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

然后这将起作用

infos.Select(SetAll).ToArray(); 

它将Select与索引器一起使用,但它并不重要。