-
-
Save jeffora/1243959 to your computer and use it in GitHub Desktop.
// Example 1: | |
// conn has a compile time type of DbConnection, as this is what is returned by OpenConnection | |
// cmd has compile time type of DbCommand, as this is what is returned by CreateCommand | |
// Both have runtime types of the more specific SqlConnection/SqlCommand | |
public static dynamic DynamicWeirdness() { | |
using (var conn = OpenConnection()) { | |
var cmd = CreateCommand("SELECT * FROM Products"); | |
cmd.Connection = conn; | |
} | |
Console.WriteLine("It worked!"); | |
Console.Read(); | |
return null; | |
} | |
// Example 2: | |
// conn has a compile time type of DbConnection, as this is what is returned by OpenConnection | |
// cmd is only evaluated at runtime due to dynamic. At runtime, it becomes the most specific SqlCommand | |
// SqlCommand.Connection requires SqlConnection and although conn's runtime type is SqlConnection, its | |
// compile-time is still DbConnection and thus you get the error | |
public static dynamic DynamicWeirdness() { | |
dynamic ex = new ExpandoObject(); | |
ex.TableName = "Products"; | |
using (var conn = OpenConnection()) { | |
var cmd = CreateCommand(ex); | |
cmd.Connection = conn; | |
} | |
Console.WriteLine("It worked!"); | |
Console.Read(); | |
return null; | |
} | |
// Example 3: | |
// conn has a compile time type of DbConnection, as this is what is returned by OpenConnection | |
// cmd is evaluated at runtime due to dynamic to be any type compatible with DbCommand. | |
// Runtime type is still SqlCommand, but compile-time is specified as DbCommand (due to not using var). | |
// DbCommand.Connection accepts DbConnection, which is the compile-time type of conn | |
public static dynamic DynamicWeirdness() { | |
dynamic ex = new ExpandoObject (); | |
ex.Query = "SELECT * FROM Products"; | |
using (var conn = OpenConnection()) { | |
DbCommand cmd = CreateCommand(ex); // <-- DON'T USE VAR | |
cmd.Connection = conn; | |
} | |
Console.WriteLine("It worked!"); | |
Console.Read(); | |
return null; | |
} |
I disagree. I assume by 'the entire block is a dynamic expression' you mean the fact that the method returns dynamic? This has nothing to do with compile time types within the method, where applicable. Furthermore, the behaviour remains when you change the method to return object
or make it a void
method. Casting to ExpandoObject
works for the same reason that removing var
and using DbCommand
works - it changes the compile-time type to DbCommand
. If you use the dynamic object as parameter, hover over the var
keyword in VS, the tooltip says its type is dynamic
. Cast ex
to ExpandoObject
and hover again, it now has a compile-time type of DbCommand
.
OK so the next twist is that if I set "using dynamic conn = OpenConnection()" it works as well. What I mean by "the entire block is an expression" means exactly that - if you hover over it the compiler has no idea what's going on - there's "too much dynamic" happening. So it hands it off to the runtime binder to figure out.
Now here's where it gets interesting. Take a look at the question again - see the last edit? See where I output the runtime types for each variable? They're SqlClient.
We're talking in circles here - but everything you're writing here is not convincing me that there's a compile-time type for the code I've written. There can't be. It's dynamic and maybe IL is taking a swing at figuring it out... but there's no way that you and I can know what that special magic is since we're not compilers.
I don't think this is the case. The reasoning is that the entire block is a dynamic expression - given that then there is no compile time type. This has nothing to do with DbCommand/SqlCommand - if I cast "ex" as ExpandoObject it still works regardless of what the compiler things the Command is.