Reputation: 19149
The following call will fail because compiler expects 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);
So that means compiler can't use this overload any way. it can't cast int
to object
.
Having this in mind, Why the following call is ambiguous?
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);
If compiler can't use overload with object any way then why its struggling here?
Here is the actual code I'm having. I can deal with this problem easily but im just being curios.
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);
Upvotes: 3
Views: 310
Reputation: 31803
This is because the System.Func<in T1, in T2, out TResult>
is contravariant in its argument types. This is denoted by the in
modifier on the corresponding type parameters. What this means is that it is a match for any function taking an argument of type T1
or any type T1
can be assigned to, and an argument of type T2
or any type T2
can be assigned to. Your first signature matches the overload of Enumerable.Select
that does not incorporate an index. However, your second signature actually matches the overload of Enumerable.Select
which does incorporate the index because int
is assignable to object
.
To demonstrate this. Simply create an arbitrary class and change your program like so.
private Action SetAll(PropertyInfo info, A a) => () => info.SetValue(this, obj);
private Action SetAll(PropertyInfo info) => () => info.SetValue(this, null);
class A {}
You will observe the error disappears as int
is not assignable to A
.
As discussed in the comments, there is a wrinkle I failed to take into account. The contravariant relationship holds between reference types and between generics that are not constrained to be value types but it specifically does not work in when directly assigning between delegates taking int
and object
Given
Func<int, Action> f;
Func<object, Action> g;
The following are both errors
g = f;
f = g;
However, if we replace int
with say some class A
Func<A, Action> f;
Func<object, Action> g;
The first is an error because an object is not an A, but the second succeeds as explained above.
g = f;
f = g;
Upvotes: 3
Reputation: 19149
In order to work with method group you can wrtie dummy variable.
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
Then this will work
infos.Select(SetAll).ToArray();
It will use Select
with indexer but it shouldnt matter much.
Upvotes: 0