Closed Grauenwolf closed 2 years ago
This is by design in the language specification: see https://github.com/dotnet/csharpstandard/blob/draft-v6/standard/expressions.md#1165-compile-time-checking-of-dynamic-member-invocation for the specification for regular method invocations. There isn't yet a specification for local functions, but the compiler can make a very specific assumption about them it cannot make about regular methods: there is no overloading. To see how this comes into play, consider this example:
// In library lib
public class C
{
public static void M(object o) => Console.WriteLine("Object");
}
// In a console app that depends on lib
C.M((dynamic)"");
In this version, there is only one M
, and the code will always print Object
. However, these are separate libraries, which means lib
can be updated at runtime to have a new overload. Update your copy of lib
to contain this instead:
public class C
{
public static void M(object o) => Console.WriteLine("Object");
public static void M(string s) => Console.WriteLine("String"); // New overload!
}
Rebuild lib
, and then copy the output dll over to the console
project and rerun your console app, without recompiling. You'll see that it now prints String
instead of Object
, as the DLR found the new method and determined it was a better match for the given argument. Since local functions can't be overloaded, the compiler doesn't have to consider this case, and can make stronger assumptions about the method group.
Arguably, the specification could call out methods that are from source specifically, and give an error in that case. However, I don't think that's a good idea, as you then have to start defining what "from source" actually means. Are two projects in the same solution both considered from source? I just demonstrated how that can actually change, even for projects in the same solution. And we don't like it when there's unintended magic that changes from within a project to without, and this would be such a case. So, we take a consistent rule and allow this case.
Version Used:
.NET 6
Steps to Reproduce:
Expected Behavior:
Function
bar
should have a compiler error just likebar2
.Actual Behavior:
Runtime error instead of compiler error. Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Cannot implicitly convert type 'void' to 'object''