I have a function whose job is to execute an expression, then catch any exceptions and to rethrow another exception based on the caught one. There is also an async variant of this function.
I want to check in a unit test project (xUnit, if that matters) if this function works well, that is, it throws the correct exception.
But I can't find a nice way to do this, since it seems that in some cases the async implementation is called with non-async code, which results in the exception not being caught.
The problem can be demonstrated with the following code:
using System;
using System.Threading.Tasks;
public class Program
{
public static void DoStuff(Action lambda)
{
Console.WriteLine("Doing stuff with Action");
lambda();
}
public async static Task DoStuff(Func<Task> lambda)
{
Console.WriteLine("Doing stuff with Func<Task>");
await lambda();
}
public async static Task Main()
{
DoStuff(() => throw new Exception("WOW")); // Here I get warning CS4014 AND the exception goes unnoticed
DoStuff(void () => throw new Exception("WOW")); // Explicit void seems unnecessary
await DoStuff(async () => await Task.Delay(1));
}
}
The output is:
Doing stuff with Func<Task>
Doing stuff with Action
Unhandled exception. System.Exception: WOW
at Program.<>c.<Main>b__2_1()
at Program.DoStuff(Action lambda)
at Program.Main()
at Program.<Main>()
https://dotnetfiddle.net/46eGYb
I'm using C# 10.0 with .NET 6 and Visual Studio 2022.
Am I missing something or is it a bug in C#? I do understand that explicitly specifying the void return type solves the problem, but it looks like to me that it could be inferred correctly.
EDIT:
After some discussion in the comments, the lesson is that the type of the first lambda can't be inferred (it wouldn't be possible to store it in a var, for example). But despite all that, the code compiles in this case.
Edit 2: this explains: https://stackoverflow.com/a/54208293/2289372
...when the parameter is an anonymous method, it will always prefer the overload who's delegate (or expression) has a return value over one that has no return value. This will be true whether it's a statement lambda or expression lambda; it applies to any form of anonymous function.