I have two extension methods on IDataReader with the following signatures:
internal static List<T> GetList<T>(this IDataReader reader, Func<string, T> del)
internal static double? GetDoubleOrNull(this IDataReader reader, string columnName)
GetDoubleOrNull
does not have any overloads.
Elsewhere, I'm able to do
Func<string, double?> del = reader.GetDoubleOrNull;
var x = reader.GetList(del);
or
var x = reader.GetList<double?>(reader.GetDoubleOrNull);
or just pass in an instance method like
public double? blah(string s)
var x = reader.GetList(blah);
but I can't do
var x = reader.GetList(reader.GetDoubleOrNull);
The compiler gives the error
cannot convert from 'method group' to 'System.Func<string,double?>'
I don't understand this. I thought that since there's no overload on GetDoubleOrNull
, there wouldn't be an overload resolution and it could infer the type parameter from the method signature.
The really confusing part is how it seems to work when passing in blah
.
Preface: Skip to the edit if you want the complete explanation. Spoiler: Extension Methods are a compiler trick, and actually have one more argument than they look like when you invoke them.
It's in the resolution of the method group. Apparently the C# compiler doesn't take the time to figure out whether the method you are using has overloads or not; it just always requires an explicit cast. Check out:
What is a method group in C#?
Method Inference does not work with method group
The method group that comes back from reader.GetDoubleOrNull
is narrowed down by what you try to cast it to: GetDoubleOrNull
could refer to any number of overloaded methods with that name. You must explicitly cast it.
Interestingly, you can't even assign a method group to an implicitly-typed variable for the same reason:
var x = reader.GetDoubleOrNull;
fails to compile because it requires an explicit cast.
Edit I'm pretty sure that the confusion here has to do with extension methods:
Check out the following test class:
public static class Extensions
{
public static List<T> GetList<T>(this IDataReader reader, Func<string, T> del)
{
throw new NotImplementedException();
}
public static double? GetDoubleOrNull(this IDataReader reader, string columnName)
{
throw new NotImplementedException();
}
public static double? blah(this string s)
{
throw new NotImplementedException();
}
}
You can successfully call
var x = reader.GetList(Extensions.blah);
Why could this be? blah
is a static extension method as well, so, based on your evidence, it would seem like the above line should not compile. Further complicating things, let's add this method:
public static List<T> GetList2<T>(this IDataReader reader, Func<IDataReader, string, T> del)
{
throw new NotImplementedException();
}
You can now call
x = reader.GetList2(Extensions.GetDoubleOrNull);
and it will compile properly. What gives?
Here's the answer: Extension Methods do not actually add methods to your objects. They're really a compiler trick to allow you to program as if those methods were part of your classes. From here:
In your code you invoke the extension method with instance method syntax. However, the intermediate language (IL) generated by the compiler translates your code into a call on the static method. Therefore, the principle of encapsulation is not really being violated. In fact, extension methods cannot access private variables in the type they are extending.
So, when you call
var x = reader.GetDoubleOrNull("myColumnName");
what is actually being compiled and executed is essentially this (a perfectly legitimate call, even though it's an extension method):
var x = Extensions.GetDoubleOrNull(reader, "myColumnName");
So, when you try to use GetDoubleOrNull
as an arg for a Func<string, double?>
, the compiler is going "I can turn GetDoubleOrNull
into a Func<IDataReader, string, double?>
because it has two arguments, but I don't know how to turn it into a Func<string, double?>
"
Even though you're calling it as if it's an instance method of the IDataReader
with one arg, it is not: it's just a static method with two args that the C# compiler has tricked you into thinking is part of IDataReader
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With