Can someone explain to me why the compiler does not check the return type of a function if a dynamic variable is used as an argument to a method call?
class Program
{
static void Main(string[] args)
{
// int a = GetAString(1); // Compiler error CS0029 Cannot impilicitly convert type 'string' to 'int'
dynamic x = 1;
int b = GetAString(x); // No compiler error -> Runtime Binder Exception
// int c = (string)GetAString(x); // Compiler error CS0029 Cannot impilicitly convert type 'string' to 'int'
}
static string GetAString(int uselessInt)
{
return "abc";
}
}
By using dynamic the compiler will generate a call site anywhere you use a dynamic parameter. This call site will attempt to resolve the method at runtime, and if it cannot find a matching method will raise an exception.
In your example the call site examines x and sees that it is an int. It then looks for any methods called GetAString that take an int and finds your method and generates code to make the call.
Next, it will generate code to attempt to assign the return value to b. All of this is still done at runtime as the use of the dynamic variable has made the entire expression require runtime evaluation. The call site will see if it can generate code to assign a string to an int, and as it cannot it will raise an exception.
As an aside, your example doesn't make a lot of sense as you seem to want to assign a string to an int Your GetAsString method is even returning a non-numeric value so it's never going to assign to an int. If you write:
dynamic x = 1;
string b = GetAsString(x);
Then everything should work.
In the general case, the candidates aren't necessarily as straightforward as yours. For example, consider these two methods:
string M(string a) => a;
char[] M(char[] a) => a;
What should this code suggest as the type of the last variable?
dynamic d = SomeExpression();
var s = M(d);
At this point, the designers of C# would have to make a choice:
Assert that the return value of a method called with dynamic arguments is also dynamic itself.
Select a type that can be assigned from all methods of the group (e.g. IEnumerable<char>).
The latter option is essentially what you're describing in your question. The C# designers went with the former option. Possible reasons for that design decision could be:
Maybe they thought that if you opt in to dynamic in an expression, then it's more likely than not that you'll want to keep using dynamic on any dependent expressions, until you explicitly opt out of it again.
Maybe they didn't introduce dynamic to enable multiple dispatch, so they didn't want to encourage it further by including provisions for static typing.
Maybe they thought that including those provisions would bloat the specification or make the language harder to understand.
Maybe the former option is simpler to implement (assuming you already have the rest of dynamic implemented) and they decided the other option wasn't worth more time or effort.
Maybe it's just not that straightforward to implement in C#. Value types could require boxing to match the common supertype, which complicates things. Raw pointer types are out of the unified hierarchy altogether.
Related
LINQPad example:
void Main()
{
One(i => PrintInteger(i));
One(PrintInteger);
Two(i => PrintInteger(i));
// Two(PrintInteger); - won't compile
}
static void One(Action<int> a)
{
a(1);
}
static void Two(Expression<Action<int>> e)
{
e.Compile()(2);
}
static void PrintInteger(int i)
{
Console.WriteLine(i);
}
Uncommenting the Two(PrintInteger); line results in an error:
cannot convert from 'method group' to
'System.Linq.Expressions.Expression<System.Action<int>>'
This is similar to Convert Method Group to Expression, but I'm interested in the "why." I understand that Features cost money, time and effort; I'm wondering if there's a more interesting explanation.
Because, in order to get the expression tree, we need a representation of the method in (uncompiled) source form. Lambda expressions are locally available in the source code and therefore are always available uncompiled. But methods may not be from inside the current assembly, and may thus be available only in compiled form.
Granted, the C# compiler could decompile the assembly’s IL code to retrieve an expression tree but as you mentioned, implementing feature costs money, this particular feature isn’t trivial, and the benefits are unclear.
There is no reason in principle. It could be done this way. The compiler could just create the lambda by itself before converting it (this is obviously always possible - it knows the exact method being called so it can just create a lambda from its parameters).
There is one catch, though. The name of the parameter of your lambda is normally hard-coded into the IL being generated. If there is no lambda, there is no name. But the compiler could either create a dummy name or reuse the names of the method being called (they are always available in the .NET assembly format).
Why didn't the C# team decide to enable this? The only reason that comes to mind is that they wanted to spend their time elsewhere. I applaud them for that decision. I'd rather have LINQ or async than this obscure feature.
In the One example, you are implicitly creating an Action<int> delegate. It's the same as:
One( new Action<int>( PrintInteger ) );
I believe this is in the language to improve the syntax for subscribing to events.
The same thing doesn't happen for Expression<T>, which is why your second example doesn't compile.
EDIT :
It's called a "method group conversion". It's in the C# spec - section 6.6
An implicit conversion (§6.1) exists from a method group (§7.1) to a compatible delegate type
Inspired by this question.
Short version: Why can't the compiler figure out the compile-time type of M(dynamic arg) if there is only one overload of M or all of the overloads of M have the same return type?
Per the spec, §7.6.5:
An invocation-expression is dynamically bound (§7.2.2) if at least one of the following holds:
The primary-expression has compile-time type dynamic.
At least one argument of the optional argument-list has compile-time type dynamic and the primary-expression does not have a delegate type.
It makes sense that for
class Foo {
public int M(string s) { return 0; }
public string M(int s) { return String.Empty; }
}
the compiler can't figure out the compile-time type of
dynamic d = // dynamic
var x = new Foo().M(d);
because it won't know until runtime which overload of M is invoked.
However, why can't the compiler figure out the compile-time type if M has only one overload or all of the overloads of M return the same type?
I'm looking to understand why the spec doesn't allow the compiler to type these expressions statically at compile time.
UPDATE: This question was the subject of my blog on the 22nd of October, 2012. Thanks for the great question!
Why can't the compiler figure out the compile-type type of M(dynamic_expression) if there is only one overload of M or all of the overloads of M have the same return type?
The compiler can figure out the compile-time type; the compile-time type is dynamic, and the compiler figures that out successfully.
I think the question you intended to ask is:
Why is the compile-time type of M(dynamic_expression) always dynamic, even in the rare and unlikely case that you're making a completely unnecessary dynamic call to a method M that will always be chosen regardless of the argument type?
When you phrase the question like that, it kinda answers itself. :-)
Reason one:
The cases you envision are rare; in order for the compiler to be able to make the kind of inference you describe, enough information must be known so that the compiler can do almost a full static type analysis of the expression. But if you are in that scenario then why are you using dynamic in the first place? You would do far better to simply say:
object d = whatever;
Foo foo = new Foo();
int x = (d is string) ? foo.M((string)d) : foo((int)d);
Obviously if there is only one overload of M then it is even easier: cast the object to the desired type. If it fails at runtime because the cast it bad, well, dynamic would have failed too!
There's simply no need for dynamic in the first place in these sorts of scenarios, so why would we do a lot of expensive and difficult type inference work in the compiler to enable a scenario we don't want you using dynamic for in the first place?
Reason two:
Suppose we did say that overload resolution has very special rules if the method group is statically known to contain one method. Great. Now we've just added a new kind of fragility to the language. Now adding a new overload changes the return type of a call to a completely different type -- a type which not only causes dynamic semantics, but also boxes value types. But wait, it gets worse!
// Foo corporation:
class B
{
}
// Bar corporation:
class D : B
{
public int M(int x) { return x; }
}
// Baz corporation:
dynamic dyn = whatever;
D d = new D();
var q = d.M(dyn);
Let's suppose that we implement your feature requiest and infer that q is int, by your logic. Now Foo corporation adds:
class B
{
public string M(string x) { return x; }
}
And suddenly when Baz corporation recompiles their code, suddenly the type of q quietly turns to dynamic, because we don't know at compile time that dyn is not a string. That is a bizarre and unexpected change in the static analysis! Why should a third party adding a new method to a base class cause the type of a local variable to change in an entirely different method in an entirely different class that is written at a different company, a company that does not even use B directly, but only via D?
This is a new form of the Brittle Base Class problem, and we seek to minimize Brittle Base Class problems in C#.
Or, what if instead Foo corp said:
class B
{
protected string M(string x) { return x; }
}
Now, by your logic,
var q = d.M(dyn);
gives q the type int when the code above is outside of a type that inherits from D, but
var q = this.M(dyn);
gives the type of q as dynamic when inside a type that inherits from D! As a developer I would find that quite surprising.
Reason Three:
There is too much cleverness in C# already. Our aim is not to build a logic engine that can work out all possible type restrictions on all possible values given a particular program. We prefer to have general, understandable, comprehensible rules that can be written down easily and implemented without bugs. The spec is already eight hundred pages long and writing a bug-free compiler is incredibly difficult. Let's not make it more difficult. Not to mention the expense of testing all those crazy cases.
Reason four:
Moreover: the language affords you many opportunities to avail yourself of the static type analyzer. If you are using dynamic, you are specifically asking for that analyzer to defer its action until runtime. It should not be a surprise that using the "stop doing static type analysis at compile time" feature causes static type analysis to not work very well at compile time.
An early design of the dynamic feature had support for something like this. The compiler would still do static overload resolution, and introduced a "phantom overload" that represents dynamic overload resolution only if necessary.
Blog post introducing phantom methods
Details on phantom methods
As you can see in the second post, this approach introduces a lot of complexity (the second article talks about how type inference would need to be modified to make the approach work out). I'm not surprised that the C# team decided to go with the simpler idea of always using dynamic overload resolution when dynamic is involved.
However, why can't the compiler figure out the compile-time type if M has only one overload or all of the overloads of M return the same type?
The compiler could potentially do this, but the language team decided not to have it work this way.
The entire purpose of dynamic is to have all expressions using dynamic execute with "their resolution is deferred until the program is run" (C# spec, 4.2.3). The compiler explicitly does not perform static binding (which would be required to get the behavior you want here) for dynamic expressions.
Having a fallback to static binding if there was only a single binding option would force the compiler to check this case - which was not added in. As for why the language team didn't want to do it, I suspect Eric Lippert's response here applies:
I am asked "why doesn't C# implement feature X?" all the time. The answer is always the same: because no one ever designed, specified, implemented, tested, documented and shipped that feature.
I think the case of being able to statically determine the only possible return type of a dynamic method resolution is so narrow that it would be more confusing and inconsistent if the C# compiler did it, rather than having across the board behavior.
Even with your example, what if Foo is part of a different dll, Foo could be a newer version at runtime from a binding redirect with additional M's that have a different return type, and then the compiler would have guessed wrong because the runtime resolution would return a different type.
What if Foo is an IDynamicMetaObjectProvider d might not match any of the static arguments and thus it would fall back on it's dynamic behavior which could possibly return a different type.
Why does this work? I'm not complaining, just want to know.
void Test()
{
int a = 1;
int b = 2;
What<int>(a, b);
// Why does this next line work?
What(a, b);
}
void What<T>(T a, T b)
{
}
It works because a and b are integers, so the compiler can infer the generic type argument for What.
In C# 3, the compiler can also infer the type argument even when the types don't match, as long as a widening conversion makes sense. For instance, if c were a long, then What(a, c) would be interpreted as What<long>.
Note that if, say, c were a string, it wouldn't work.
The C# compiler supports type inference for generics, and also commonly seen if you are using the var keyword.
Here int is inferred from the context (a and b) and so <int> is not needed. It keeps code cleaner and easier to read at times.
Sometimes your code may be more clear to read if you let the compiler infer the type, sometimes it may be more clear if you explicitly specify the type. It is a judgement call on your given situation.
It's using type inference for generic methods. Note that this has changed between C# 2 and 3. For example, this wouldn't have worked in C# 2:
What("hello", new object());
... whereas it would in C# 3 (or 4). In C# 2, the type inference was performed on a per-argument basis, and the results had to match exactly. In C# 3, each argument contributes information which is then put together to infer the type arguments. C# 3 also supports multi-phase type inference where the compiler can work out one type argument, then see if it's got any more information on the rest (e.g. due to lambda expressions with implicit parameter types). Basically it keeps going until it can't get any more information, or it finishes - or it sees contradictory information. The type inference in C# isn't as powerful as the Hindley-Milner algorithm, but it works better in other ways (in particular it always makes forward progress).
See section 7.4.2 of the C# 3 spec for more information.
The compiler infers the generic type parameter from the types of the actual parameters that you passed.
This feature makes LINQ calls much simpler. (You don't need to write numbers.Select<int, string>(i => i.ToString()), because the compiler infers the int from numbers and the string from ToString)
The compiler can infer type T to be an int since both parameters passed into What() are of type int. You'll notice a lot of the Linq extensions are defined with generics (as IEnumerable) but are typically used in the manner you show.
If the subject of how this works in C# 3.0 is interesting to you, here's a little video of me explaining it from back in 2006, when we were first designing the version of the feature for C# 3.0.
http://blogs.msdn.com/ericlippert/archive/2006/11/17/a-face-made-for-email-part-three.aspx
See also the "type inference" section of my blog:
http://blogs.msdn.com/ericlippert/archive/tags/Type+Inference/default.aspx
The compiler is smart enough to figure out that the generic type is 'int'
How good is C# type inference? I read somewhere that it's only for local variables? Does it work for class level attributes? For method signatures? Method return types? etc.
There are a few main kinds of type inference in C#:
Implicitly typed local variables:
Only for local variables
Only when the value is assigned as part of the declaration
Value cannot be null
Value cannot be a lambda expression, anonymous method or method group (without a cast)
The compile-time type of the value is used for the type of the variable
Any further uses of the variable are only checked against the type determined by the initial declaration+assignment; they don't contribute to the inference itself.
Generic method type argument inference, i.e. you don't specify the type arguments in a call to a generic method, the compiler figures them out based on the arguments.
Would be really handy to have this for generic types as well as generic methods
Really handy anyway - LINQ would be hard or impossible to use without it
Anonymous types would be fairly useless without it
Really complicated rules, even the spec is wrong in a few places
Lambda expression parameter type inference
Compiler tries to work out the types of the parameters for lambda expressions based on the context in which it's used
Usually works pretty well, in my experience
Array type inference, e.g. new[] { "Hi", "there" } instead of new string[] { "Hi", "there" }
Various small restrictions, nothing major
I've probably forgotten some other features which might be called "type inference". I suspect you're mostly interested in the first, but the others might be relevant to you too :)
It can only be used for local variables, but it can detect the type in many different forms.
var myVar = SomeMethodThatReturnsInt(); //will know it's an int
var myIntList = new List<int>(); //this works too (although this is technically not type inference)
var myOwnVar = new { Name = "John", Age = 100 }; // will create own type and infer that
EDIT: One more example of Tye Inference is with Lambdas. IE:
var myList = new List<int>();
//add some values to list
int x = myList.Find(i => i == 5); // compiler can infer that i is an int.
It works only with local variables as I understand it.
When C# 4.0 comes out and we have the dynamic keyword as described in this excellent presentation by Anders Hejlsberg, (C# is evolving faster than I can keep up.. I didn't have much time to acquaint myself with the var keyword)
Would I still need the var keyword ? Is there anything that var can do.. that dynamic can't?
var x = SomeFunctionThatIKnowReturnsSomeKindOfList();
// do something with x
dynamic x = SomeFunctionThatIKnowReturnsSomeKindOfList();
// do something with x
No, they're very different.
var means "infer the type of the variable at compile-time" - but it's still entirely statically bound.
dynamic means "assume I can do anything I want with this variable" - i.e. the compiler doesn't know what operations are available, and the DLR will work out what the calls really mean at execution time.
I expect to use dynamic very rarely - only when I truly want dynamic behaviour:
var lets you catch typos etc at compile-time
statically bound code is always going to run faster than dynamically bound code (even if the difference becomes reasonably small)
statically bound code gives more compile-time support beyond just errors: you can find call hierarchies, refactoring will work better, Intellisense is available etc
Dynamic and var represent two completely different ideas.
var
Var essentially asks the compiler to figure out the type of the variable based on the expression on the right hand side of the assignment statement. The variable is then treated exactly as if it were explicitly declared as the type of the expression. For example the following two statements are equivalent
var a = "foo";
string a = "foo";
The key to take away here is that "var" is 100% type safe and is a compile time operation
dynamic
Dynamic is in many ways the exact opposite of var. Using dynamic is essentially eliminating all type safety for thet particular variable. It many ways it has no type. When you call a method or field on the variable, the determination on how to invoke that field occurs at runtime. For example
dynamic d = SomeOperation();
d.Foo(); // Will this fail or not? Won't know until you run the program
The key to take away here is that "dynamic" is not type safe and is a runtime operation
Yes you will still need var:
Var is a variable whose type will be inferred by the compiler.
dynamic will have its type assigned at runtime
So:
Var i = "Hello World"
will have its type inferred as a string type in doing so intellisence will give you all the methods that string can use like,
i.Split("/")
Where as:
dynamic i = "Hello World"
won't have its type inferred untill runtime because the complier dosn't know what type it is yet, but will still let you do:
i.Split("/")
but when it calls the method that you need it may fail because the type is wrong and the method isn't there.