3

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

2 回答 2

3

这是因为System.Func<in T1, in T2, out TResult>它的参数类型是逆变的。这由in相应类型参数上的修饰符表示。这意味着它与任何接受类型参数或可以分配任何类型的函数的函数匹配T1T1并且可以分配类型T2或任何类型的参数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不可分配给A.

正如评论中所讨论的,我没有考虑到一个皱纹。int逆变关系在引用类型之间以及不受限制为值类型的泛型之间保持不变,但在委托采取和object 给定之间直接分配时它特别不起作用

Func<int, Action> f;

Func<object, Action> g;

以下都是错误

g = f;
f = g;

但是,如果我们int用一些类替换A

Func<A, Action> f;

Func<object, Action> g;

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

g = f;
f = g;
于 2017-01-02T15:58:59.590 回答
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与索引器一起使用,但它应该无关紧要。

于 2017-01-02T16:00:34.213 回答