-
-
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; | |
} |
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 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 avoid
method. Casting toExpandoObject
works for the same reason that removingvar
and usingDbCommand
works - it changes the compile-time type toDbCommand
. If you use the dynamic object as parameter, hover over thevar
keyword in VS, the tooltip says its type isdynamic
. Castex
toExpandoObject
and hover again, it now has a compile-time type ofDbCommand
.