I was implementing sync/async overloads when I came across this peculiar situation:
When I have a regular lambda expression without parameters or a return value it goes to the Run overload with the Action parameter, which is predictable. But when that lambda has a while (true) in it it goes to the overload with the Func parameter.
public void Test()
{
Run(() => { var name = "bar"; });
Run(() => { while (true) ; });
}
void Run(Action action)
{
Console.WriteLine("action");
}
void Run(Func<Task> func) // Same behavior with Func<T> of any type.
{
Console.WriteLine("func");
}
Output:
action
func
So, how can that be? Is there a reason for it?
So to start with, the first expression can only possibly call the first overload. It is not a valid expression for a Func<Task> because there is a code path that returns an invalid value (void instead of Task).
() => while(true) is actually a valid method for either signature. (It, along with implementations such as () => throw new Expression(); are valid bodies of methods that return any possible type, including void, an interesting point of trivia, and why auto generated methods from an IDE typically just throw an exception; it'll compile regardless of the signature of the method.) A method that loops infinitely is a method in which there are no code paths that don't return the correct value (and that's true whether the "correct value" is void, Task, or literally anything else). This is of course because it never returns a value, and it does so in a way that the compiler can prove. (If it did so in a way that the compiler couldn't prove, as it hasn't solved the halting problem after all, then we'd be in the same boat as A.)
So, for our infinite loop, which is better, given that both overload are applicable. This brings us to our betterness section of the C# specs.
If we go to section 7.4.3.3, bullet 4, we see:
If E is an anonymous function, T1 and T2 are delegate types or expression tree types with identical parameter lists, and an inferred return type X exists for E in the context of that parameter list (§7.4.2.11):
[...]
if T1 has a return type Y, and T2 is void returning, then C1 is the better conversion.
So when converting from an anonymous delegate, which is what we're doing, it will prefer the conversion that returns a value over one that is void, so it chooses Func<Task>.
Related
I want to write a function that takes a function as an argument and then do different things based on whether the passed-in function returns void vs a value.
C# signature checking can't tell the difference so I'm stuck doing it in code.
Is there an easy way to test whether an arbitrary function returns void?
To be clear. I explicitly am not interested in a compile error. I just want the equivalent of what I can do for any other object.
void IsString(object o) => o is string;
void ElseWhere() {
object o = 1;
if (IsString(o)) Bla();
However even this gets a compile error claiming the two methods are ambiguous. It doesn't flag the methods themselves ambiguous but I get an error on the call saying it can't resolve between them.
private static bool HasNoReturnValue(Action o) => true;
private static bool HasNoReturnValue(Func<object> o) => false;
...
if (HasNoReturnValue(SomeFunction)) Bla();
As do anything I've tried involving typeof:
if (SomeFunction is typeof(Func(object>)) Bla();
Let's say you have two methods, one of which returns a Boolean and one which returns void.
void SomeFunction1()
{
}
bool SomeFunction2()
{
return false;
}
To pass either of these as a pointer to a method, you have to convert them to a delegate. Two types of delegates: Action and Func<bool>, respectively:
var action1 = new Action(SomeFunction1);
var action2 = new Func<bool>(SomeFunction2);
You can then write two methods that accept these types as arguments:
void AcceptDelegate(Action action)
{
Console.WriteLine("The delegate returns void.");
}
void AcceptDelegate(Func<bool> func)
{
Console.WriteLine("The delegate returns a Boolean.");
}
And call them like this:
AcceptDelegate(action1);
AcceptDelegate(action2);
Or you could pass the method group directly and the compiler will figure out the type (Why? See the Microsoft documentation on c# method group conversions):
AcceptDelegate(SomeFunction1);
AcceptDelegate(SomeFunction2);
Either way you call them, you would get this output:
The delegate returns void.
The delegate returns a Boolean.
The reason this works is the compiler will automatically pick the right one at compile-time, based on the type of the delegate, just as it would pick the overload for any type such as string or integer. This is the type-safe / early-bound way to do it.
If you insist on an "any delegate"/ late binding sort of approach, you could do something like this:
void AcceptAnyDelegate(Delegate anyAction)
{
Console.WriteLine("The function returns a {0}", anyAction.Method.ReturnType);
}
Because the signature isn't type specific, you have to pass the specific delegates this time (Why? See this answer):
AcceptAnyDelegate(action1);
AcceptAnyDelegate(action2);
And the output would be:
The function returns a Void
The function returns a Boolean
Edit
After rereading your comments, I believe the confusion here is due to a misunderstanding of method groups and delegates.
When you write something like this:
Foo(Bar);
...it appears you believe you are passing to Foo a direct reference to the Bar method. That is not correct. What you are doing is specifying a method group, which the compiler can then use to infer the type of delegate to pass. If Bar is a method with no inputs or outputs, the above code is exactly the same as
Foo(new Action( Bar ));
...only the creation of the delegate is hidden from you by the compiler.
All delegates are specifically typed with respect to their parameters and return type. The Delegate base type is abstract and cannot exist in concrete form. So there is no such thing as passing a type-agnostic function reference-- it doesn't exist in c#.
If you really really want to pass something that is type-agnostic, you can ask the caller to pass a lambda expression:
Foo( () => SomeFunction1() );
You could then parse the expression to figure out the method's inputs and outputs:
void Foo(Expression<Action> anyAction)
{
var mce = anyAction.Body as MethodCallExpression;
var method = mce.Method;
Console.WriteLine("The method has a return type of {0}", method.ReturnType.Name);
}
Then to invoke the expression you would use:
var compiled = anyAction.Compile();
compiled();
That is the closest you're going to get.
There's two different types here:
Action for no return type
and
Func for a return type
Can you make two different signatures for these two different argument types?
This will do the trick
public static void TakeInAFunc<T>(T aFuncOrAction)
{
if (typeof(T) == typeof(Func<>))
{
// some value returned.
}
else if (typeof(T) == typeof(Action<>))
{
// it returns void.
}
}
Using xUnit's Assert.Throws, I stumbled upon this (to me) hard to explain overload resolution issue. In xUnit, this method is marked obsolete:
[Obsolete("You must call Assert.ThrowsAsync<T> (and await the result) " +
"when testing async code.", true)]
public static T Throws<T>(Func<Task> testCode) where T : Exception
{ throw new NotImplementedException(); }
The question is, why does an inline statement lambda (or expression) that simply throws an exception resolve to this overload (and therefore doesn't compile)?
using System;
using Xunit;
class Program
{
static void Main(string[] args)
{
// this compiles (of course)
// resolves into the overload accepting an `Action`
Assert.Throws<Exception>(() => ThrowException());
// line below gives error CS0619 'Assert.Throws<T>(Func<Task>)' is obsolete:
// 'You must call Assert.ThrowsAsync<T> (and await the result) when testing async code.'
Assert.Throws<Exception>(() => { throw new Exception(); });
}
static void ThrowException()
{
throw new Exception("Some message");
}
}
I was able to reproduce this, given the function declarations:
static void CallFunction(Action action) { }
static void CallFunction(Func<Task> func) { }
And calling them:
CallFunction(() => ThrowException());
CallFunction(() => { throw new Exception(); });
The second one resolves into CallFunction(Func<Task> func) overload. The weird thing is if I change the body like this:
CallFunction(() => { int x = 1; });
It resolves to CallFunction(Action action) overload.
If the last statement in the body is a throw statement, I guess the compiler thinks method is returning something, and picks the closest -more specific- overload to that scenario which is Func<Task>.
The closest thing I found in the documentation is this:
7.5.2.12 Inferred return type
• If F is async and the body of F is either an expression classified as nothing (§7.1), or a statement block where no return statements have expressions, the inferred return type is System.Threading.Tasks.Task
The function here is a statement block, but it's not async though. Note that I'm not saying this exact rule applies here. Still I'm guessing it's related to this.
This article from Eric Lippert explains it better. (thanks for the comment #Damien_The_Unbeliever).
Here's a complete example which doesn't involve Task, to remove any hint of asynchrony being involved:
using System;
class Program
{
static void Method(Action action)
{
Console.WriteLine("Action");
}
static void Method(Func<int> func)
{
Console.WriteLine("Func<int>");
}
static void ThrowException()
{
throw new Exception();
}
static void Main()
{
// Resolvse to the Action overload
Method(() => ThrowException());
// Resolves to the Func<int> overload
Method(() => { throw new Exception(); });
}
}
Using section numbering from ECMA 334 (5th edition), we're interested in section 12.6.4 - overload resolution. The two important steps are:
Identify applicable methods (12.6.4.2)
Identify the best method (12.6.4.3)
We'll look at each call in turn
Call 1: () => ThrowException()
Let's start with the first call, which has an argument of () => ThrowException(). To check for applicability, we need a conversion from that argument to either Action or Func<int>. We can check that without involving overloading at all:
// Fine
Action action = () => ThrowException();
// Fails to compile:
// error CS0029: Cannot implicitly convert type 'void' to 'int'
// error CS1662: Cannot convert lambda expression to intended delegate type because
// some of the return types in the block are not implicitly convertible to the
// delegate return type
Func<int> func = () => ThrowException();
The CS1662 error is a little unfortunately worded in this case - it's not that there's a return type in the block that's not implicitly convertible to the delegate return type, it's that there isn't a return type in the lambda expression at all. The spec way of preventing this is in section 11.7.1. None of the permitted conversions there work. The closest is this (where F is the lambda expression and D is Func<int>):
If the body of F is an expression, and either F is non-async and D has a non-void return type T, or F is async and D has a return type Task<T>, then when each parameter of F is given the type of the corresponding parameter in D, the body of F is a valid expression (w.r.t §12) that is implicitly convertible to T.
In this case the expression ThrowException is not implicitly convertible to int, hence the error.
All of this means that only the first method is applicable for () => ThrowException(). Our pick for "best function member" is really easy when the set of applicable function members only has a single entry...
Call 2: () => { throw new Exception(); }
Now let's look at the second call, which has () => { throw new Exception(); } as the argument. Let's try the same conversions:
// Fine
Action action = () => { throw new Exception(); };
// Fine
Func<int> func = () => { throw new Exception(); };
Both conversions work here. The latter one works because of this bullet from 11.7.1:
If the body of F is a statement block, and either F is non-async and D has a
non-void return type T, or F is async and D has a return type Task<T>,
then when each parameter of F is given the type of the corresponding parameter in
D, the body of F is a valid statement block (w.r.t §13.3) with a nonreachable
end point in which each return statement specifies an expression that is implicitly
convertible to T.
I realize it sounds odd that this works, but:
The end point of the block is not reachable
There are no return statements, so the condition of "each return statement specifies [...]" is indeed met
To put it another way: you could use that block as the body of a method that's declared to return int.
That means both our methods are applicable in this case.
So which is better?
Now we need to look at section 12.6.4.3 to work out which method will actually be picked.
There are lots of rules here, but the one that decides things here is the conversion from the lambda expression to either Action or Func<int>. That's resolved in 12.6.4.4 (better conversion from expression):
Given an implicit conversion C1 that converts from an expression E to a type T1, and an implicit conversion C2 that converts from an expression E to a type T2, C1 is a better conversion than C2 if at least one of the following holds:
...
E is an anonymous function, T1 is either a delegate type D1 or an expression tree
type Expression<D1>, T2 is either a delegate type D2 or an expression tree type Expression<D2> and
one of the following holds:
D1 is a better conversion target than D2
D1 and D2 have identical parameter lists, and one of the following holds:
D1 has a return type Y1, and D2 has a return type Y2, an inferred return type X exists for E in the context of that parameter list (§12.6.3.13), and the conversion from X to Y1 is better than the conversion from X to Y2
E is async [... - skipped because it's not]
D1 has a return type Y, and D2 is void returning
The part I've put in bold is the important bit. When you consider the following scenario:
E is () => { throw new Exception(); }
T1 is Func<int> (so D1 is Func<int> too)
T2 is Action (so D2 is Action too)
... then both D1 and D2 have empty parameter lists, but D1 has a return type int, and D2 is void returning.
Therefore the conversion to Func<int> is better than the conversion to Action... which means that Method(Action) is a better function member than Member(Func<int>) for the second call.
Phew! Don't you just love overload resolution?
Having these three methods available in Rx.NET library
public static IObservable<TResult> Create<TResult>(Func<IObserver<TResult>, CancellationToken, Task> subscribeAsync) {...}
public static IObservable<TResult> Create<TResult>(Func<IObserver<TResult>, CancellationToken, Task<IDisposable>> subscribeAsync) {...}
public static IObservable<TResult> Create<TResult>(Func<IObserver<TResult>, CancellationToken, Task<Action>> subscribeAsync) {...}
I write the following sample code in MSVS 2013:
var sequence =
Observable.Create<int>( async ( observer, token ) =>
{
while ( true )
{
token.ThrowIfCancellationRequested();
await Task.Delay( 100, token );
observer.OnNext( 0 );
}
} );
This does not compile due to ambiguous overloads. Exact output from the compiler being:
Error 1 The call is ambiguous between the following methods or properties:
'System.Reactive.Linq.Observable.Create<int>(System.Func<System.IObserver<int>,System.Threading.CancellationToken,System.Threading.Tasks.Task<System.Action>>)'
and
'System.Reactive.Linq.Observable.Create<int>(System.Func<System.IObserver<int>,System.Threading.CancellationToken,System.Threading.Tasks.Task>)'
However as soon as I replace while( true ) with while( false ) or with var condition = true; while( condition )...
var sequence =
Observable.Create<int>( async ( observer, token ) =>
{
while ( false ) // It's the only difference
{
token.ThrowIfCancellationRequested();
await Task.Delay( 100, token );
observer.OnNext( 0 );
}
} );
the error disappears and method call resolves to this:
public static IObservable<TResult> Create<TResult>(Func<IObserver<TResult>, CancellationToken, Task> subscribeAsync) {...}
What is going on there?
This is a fun one :) There are multiple aspects to it. To start with, let's simplify it very significantly by removing Rx and actual overload resolution from the picture. Overload resolution is handled at the very end of the answer.
Anonymous function to delegate conversions, and reachability
The difference here is whether the end-point of the lambda expression is reachable. If it is, then that lambda expression doesn't return anything, and the lambda expression can only be converted to a Func<Task>. If the end-point of the lambda expression isn't reachable, then it can be converted to any Func<Task<T>>.
The form of the while statement makes a difference because of this part of the C# specification. (This is from the ECMA C# 5 standard; other versions may have slightly different wording for the same concept.)
The end point of a while statement is reachable if at least one of the following is true:
The while statement contains a reachable break statement that exits the while statement.
The while statement is reachable and the Boolean expression does not have the constant value true.
When you have a while (true) loop with no break statements, neither bullet is true, so the end point of the while statement (and therefore the lambda expression in your case) is not reachable.
Here's a short but complete example without any Rx involved:
using System;
using System.Threading.Tasks;
public class Test
{
static void Main()
{
// Valid
Func<Task> t1 = async () => { while(true); };
// Valid: end of lambda is unreachable, so it's fine to say
// it'll return an int when it gets to that end point.
Func<Task<int>> t2 = async () => { while(true); };
// Valid
Func<Task> t3 = async () => { while(false); };
// Invalid
Func<Task<int>> t4 = async () => { while(false); };
}
}
We can simplify even further by removing async from the equation. If we have a synchronous parameterless lambda expression with no return statements, that's always convertible to Action, but it's also convertible to Func<T> for any T if the end of the lambda expression isn't reachable. Slight change to the above code:
using System;
public class Test
{
static void Main()
{
// Valid
Action t1 = () => { while(true); };
// Valid: end of lambda is unreachable, so it's fine to say
// it'll return an int when it gets to that end point.
Func<int> t2 = () => { while(true); };
// Valid
Action t3 = () => { while(false); };
// Invalid
Func<int> t4 = () => { while(false); };
}
}
We can look at this in a slightly different way by removing delegates and lambda expressions from the mix. Consider these methods:
void Method1()
{
while (true);
}
// Valid: end point is unreachable
int Method2()
{
while (true);
}
void Method3()
{
while (false);
}
// Invalid: end point is reachable
int Method4()
{
while (false);
}
Although the error method for Method4 is "not all code paths return a value" the way this is detected is "the end of the method is reachable". Now imagine those method bodies are lambda expressions trying to satisfy a delegate with the same signature as the method signature, and we're back to the second example...
Fun with overload resolution
As Panagiotis Kanavos noted, the original error around overload resolution isn't reproducible in Visual Studio 2017. So what's going on? Again, we don't actually need Rx involved to test this. But we can see some very odd behavior. Consider this:
using System;
using System.Threading.Tasks;
class Program
{
static void Foo(Func<Task> func) => Console.WriteLine("Foo1");
static void Foo(Func<Task<int>> func) => Console.WriteLine("Foo2");
static void Bar(Action action) => Console.WriteLine("Bar1");
static void Bar(Func<int> action) => Console.WriteLine("Bar2");
static void Main(string[] args)
{
Foo(async () => { while (true); });
Bar(() => { while (true) ; });
}
}
That issues a warning (no await operators) but it compiles with the C# 7 compiler. The output surprised me:
Foo1
Bar2
So the resolution for Foo is determining that the conversion to Func<Task> is better than the conversion to Func<Task<int>>, whereas the resolution for Bar is determining that the conversion to Func<int> is better than the conversion to Action. All the conversions are valid - if you comment out the Foo1 and Bar2 methods, it still compiles, but gives output of Foo2, Bar1.
With the C# 5 compiler, the Foo call is ambiguous by the Bar call resolves to Bar2, just like with the C# 7 compiler.
With a bit more research, the synchronous form is specified in 12.6.4.4 of the ECMA C# 5 specification:
C1 is a better conversion than C2 if at least one of the following holds:
...
E is an anonymous function, T1 is either a delegate type D1 or an expression tree type Expression, T2 is either a delegate type D2 or an expression tree type Expression and one of the following holds:
D1 is a better conversion target than D2 (irrelevant for us)
D1 and D2 have identical parameter lists, and one of the following holds:
D1 has a return type Y1, and D2 has a return type Y2, an inferred return type X exists for E in the context of that parameter list (§12.6.3.13), and the conversion from X to Y1 is better than the conversion from X to Y2
E is async, D1 has a return type Task<Y1>, and D2 has a return type Task<Y2>, an inferred return type Task<X> exists for E in the context of that parameter list (§12.6.3.13), and the conversion from X to Y1 is better than the conversion from X to Y2
D1 has a return type Y, and D2 is void returning
So that makes sense for the non-async case - and it also makes sense for how the C# 5 compiler isn't able to resolve the ambiguity, because those rules don't break the tie.
We don't have a full C# 6 or C# 7 specification yet, but there's a draft one available. Its overload resolution rules are expressed somewhat differently, and the change may be there somewhere.
If it's going to compile to anything though, I'd expect the Foo overload accepting a Func<Task<int>> to be chosen over the overload accepting Func<Task> - because it's a more specific type. (There's a reference conversion from Func<Task<int>> to Func<Task>, but not vice versa.)
Note that the inferred return type of the lambda expression would just be Func<Task> in both the C# 5 and draft C# 6 specifications.
Ultimately, overload resolution and type inference are really hard bits of the specification. This answer explains why the while(true) loop makes a difference (because without it, the overload accepting a func returning a Task<T> isn't even applicable) but I've reached the end of what I can work out about the choice the C# 7 compiler makes.
In addition to the answer from #Daisy Shipton I'd like to add that the same behavior can be observed in the following case, too:
var sequence = Observable.Create<int>(
async (observer, token) =>
{
throw new NotImplementedException();
});
basically because of the same reason - the compiler sees that the lambda function never returns so any return type would match, which in turn makes the lambda match any of the Observable.Create overloads.
And, finally, an example of simple solution: you can cast the lambda to the desired signature type to hint the compiler which Rx overload to choose.
var sequence =
Observable.Create<int>(
(Func<IObserver<int>, CancellationToken, Task>)(async (observer, token) =>
{
throw new NotImplementedException();
})
);
There's a similar question here:
Pass Method as Parameter using C#
Which assumes you know the method's arguments and return type. I'd like to do something a bit different. I'm looking to create a version of System.Reflection's .GetMethod(string) that instead takes a lambda function - so instead of:
MethodInfo methodInfo = typeof(MyClass).GetMethod("AddThing");
I could use a more compile-safe:
MethodInfo methodInfo = ReflectionHelper.GetMethod<MyClass>(mc => mc.AddThing);
So if ReflectionHelper knew the argument count and return type beforehand the answer would be simple-ish - for example if it had no arguments and returned string:
public static MethodInfo GetMethod<T, TReturn>(Expression<Func<T, Func<TArg, TReturn>>> expr)
{
return ((MethodCallExpression)expr.Body).Method;
}
Except that I don't know the argument count/return type beforehand, and I'd like to avoid just spamming it with 20 overloads that cover most but not all cases.
So, how do I do this?
You haven't specified a method in the lambda mc => mc.AddThing - you've specified a method group.
What you could do is write a method call in the expression - although it will never be executed, so the parameters just have to specify the overload you want.
So, to use it, you would write:
MethodInfo mi = GetMethod<MyClass>( mc => mc.AddThing( null ) );
Then the expression could just be an Action, not a Func:
public static MethodInfo<T>( Expression<Action<T>> expr )
{
return ( ( MethodCallExpression ) x.Body ).Method;
}
As always with expression inspection, this is prone to runtime errors if the lambda isn't of the expected form, but it's a step in the right direction to get rid of "magic" strings.
The workaround I'm using for now, that I'll call "the spam approach," is ugly but functional in a limited set of scenarios:
public class ReflectionHelper<T>
{
public MethodInfo GetMethod<TA1>(Expression<Func<T, Func<TA1>>> expr)
{
return ((MethodCallExpression)expr.Body).Method;
}
public MethodInfo GetMethod<TA1, TA2>(Expression<Func<T, Action<TA1, TA2>>> expr)
{
return ((MethodCallExpression)expr.Body).Method;
}
public MethodInfo GetMethod<TA1, TA2, TA3>(Expression<Func<T, Action<TA1, TA2, TA3>>> expr)
{
return ((MethodCallExpression)expr.Body).Method;
}
. . . // And more
}
And it goes on, to specify a long list of potential Actions and Funcs with various argument counts. This allows me to in fact use it like:
(new ReflectionHelper<MyClass>()).GetMethod<string>(mc => mc.SaySomething).Invoke("Hi");
So it's close-ish - I get to just call the method by name, and I do avoid passing fake arguments in order to do so, but I still get caught up laying out what the Types of its arguments are - more complex than I hoped for. In a situation with multiple method overloads of the same name, passing the types of the arguments would however narrow the method list down to one specific overload.
I'd still be interested in something even simpler that can just take a method as in my Question however. It would be vague in method overload situations, but so is the standard .GetMethod(string).
This may have been answered before. I see many "dynamic method overload resolution" questions, but none that deal specifically with passing a dynamic argument. In the following code, in Test, the last call to M cannot be resolved (it doesn't compile). The error is: the call is ambiguous between [the first two overloads of M].
static void M(Func<int> f) { }
static void M(Func<string> f) { }
static void M(Func<dynamic> f) { }
static dynamic DynamicObject() {
return new object();
}
static void Test() {
M(() => 0);
M(() => "");
M(() => DynamicObject()); //doesn't compile
}
Why, since the type isn't statically known, does it not resolve to the overload accepting dynamic?
Is it even possible for an overloaded method to use dynamic?
What is the best way to resolve this?
The problem here is type inference. The compiler is trying to find out which overload to use based on the argument, but it's also trying to find out what the type of the argument is based on the chosen overload. In the case of M(() => DynamicObject()), the process goes something like this:
The argument to the method is a lambda with zero parameters. This gives us all three overloads as possibilities.
The body of the lambda returns dynamic. Because there is an implicit conversion from dynamic to any other type, we now know all three overloads are good.
Try choosing the best overload. In most cases, “best” means the most derived type. Because both int and string derive from object, the overloads with int and string are considered best.
We now have two “best” overloads, which means the compiler can't actually choose one of them. The compilation fails.
Now, regarding possible solutions to your problem:
Make the type of the lambda explicit, either using cast or typed local variable:
M((Func<dynamic>)(() => DynamicObject()));
or
Func<dynamic> f = () => DynamicObject();
M(f);
Rename the dynamic overload to something like DynamicM. This way, you don't have to deal with overload resolution.
This one feels somewhat wrong to me: make sure the dynamic overload is the only one that fits, by casting to object:
M(() => (object)DynamicObject())
From the definition in MSDN:
dynamic
Type dynamic behaves like type object in most circumstances. However,
operations that contain expressions of type dynamic are not resolved
or type checked by the compiler. The compiler packages together
information about the operation, and that information is later used to
evaluate the operation at run time. As part of the process, variables
of type dynamic are compiled into variables of type object. Therefore,
type dynamic exists only at compile time, not at run time
So dynamic doesn't exist when you compile, cause it needs to convert it into destination *type*, and that's why is not able to resolve it. What is destination type ?
In fact if you do something like this:
static void M(Func<int> f) { }
static void M(Func<string> f) { }
static void M(Func<object> f) { } // could be also declared like dynamic here, works by the way
static object DynamicObject()
{
return new object();
}
static void Test()
{
M(() => 0);
M(() => "");
M(() => DynamicObject());
}
It perfectly works as you want, as object is present like a type already at compile time, in difference of dynamic type which have to be converted.