Why does a method invocation expression have type dynamic even when there is only one possible return type?
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.
Why does a method that returns a type result in an implicit typing of dynamic?
Why does a method that returns a type result in an implicit typing of dynamic?
Because that's the best the compiler can do, given the information it has.
The reason methodResult
is dynamic
is that the entire expression used to initialize it is dynamic
. And that's the case, because data
is dynamic.
When you use dynamic
, you're telling the compiler to not resolve types at compiler time. Instead, they should be resolved according to the normal compiler rules, but at run-time.
The fluentClass
variable could hold some implementation of FluentClass
that contains an overload that matches the run-time type of the argument data
. In that case, a different implementation of SomeMethod()
could be called, returning a different type.
You've told the compiler to defer type resolution to run-time, so it can't force things back into a strongly-typed context unless you tell it explicitly what type things are. In your example, it can't, so the type remains dynamic
.
Note that you might have thought that the compiler would identify the one overload you've provided, based on its parameter type of dynamic
. But that's not how dynamic
works. The dynamic
parameter affects only the implementation of the method, i.e. the code in its body. As far as calling the method goes, the parameter is essentially object
. It's just that when the parameter value is used in the method body, it has the features of dynamic
.
Another way to think of dynamic
is that it accepts any object as input (like object
), but then allows you to use any member of that object that you believe exists (if it doesn't exist an exception will be thrown at run-time). Using dynamic
defers the compiler logic downstream, i.e. for the output of any usages of the dynamic
variable, but doesn't affect the input, i.e. the assignment of that variable.
Note also that even if the method call is completely unambiguous, e.g. a static
method where there's only one method with that name, you'll still get a dynamic
result. Once you start using dynamic
, it sticks with you until you provide an explicit cast to get back to a known type.
Related reading:
Very similar to your question, if not actually duplicates:
Why does a method invocation expression have type dynamic even when there is only one possible return type?
Why does this method keep returning dynamic despite the return type in the signature?
Why doesn't this string.Format() return string, but dynamic?
More general discussion of dynamic
:
What is the 'dynamic' type in C# 4.0 used for?
C# 4: Real-World Example of Dynamic Types
Why does compiler infer var to be dynamic instead of concrete type?
With dynamic
, all method calls are resolved at runtime. Therefore, it declines to guess at compile time what method is actually being called when you call Chars()
, Count()
, or even ToString()
. It could be anything, returning anything. This is often called "dynamic contagion".
For all the compiler knows, somtimes value.ToString()
will return MyRandomPOCOClass
, and at runtime it'll be able to dig up some overload like Tuple<int,String> Chars(MyRandomPOCOClass x)
. Maybe next time value.ToString()
will return int
. All bets are off. dynamic
turns C# into a scripting language.
Here's an example of dynamic runtime overload behavior (here's a fiddle):
public static void Main()
{
dynamic x = "foo";
Test(x);
x = 34;
Test(x);
}
public static void Test(string s)
{
Console.WriteLine("String " + s);
}
public static void Test(int n)
{
Console.WriteLine("Int " + n);
}
Output:
String foo
Int 34
Dynamic arguments and return type
You cannot call extension methods on object of type dynamic
. Extension methods are a purely compile time construct. At runtime, it will search for an instance method called FirstOrDefault
, not find one, and therefore error out.
Your solutions are to either cast the result of Query
to the appropriate type before calling FirstOrDefault
on it, or to not use the extension method syntax and instead write out: Queryable.FirstOrDefault(Query<T>(...))
which will tell the runtime binder that it's trying to bind the result of Query
to the appropriate static method, rather than an instance method.
As for why any method accepting arguments of type dynamic
always resolves to an expression of type dynamic
: that's simply what the specs say to do. The whole point of using dynamic
is to defer binding of the methods until runtime, so it cannot be sure of what the value of that expression will be until runtime; to get around that, it needs to propagate the dynamic
type.
Why is the surrounding type of a dynamic expression not statically resolved in C#?
Suppose you had
static string foo(object x) { return "bar"; }
static string foo(string x) { return "foo"; }
static void Main()
{
dynamic x = null;
foo(x); // foo(x) is a dynamic expression
}
In that case the compiler would not be able to resolve the type of the expression. While I think in your example the type should be resolvable it will not be in the cases where it is most useful, making the feature pretty cost ineffective to implement.
In addition, the DLR can't perform any binding on a null reference as in your example.
Why does this method keep returning dynamic despite the return type in the signature?
I strongly suspect that either child1
or child1.kind
are of type dynamic
, meaning that the expression is deemed to be a dynamically-bound expression, despite everything else.
Here's a short but complete example to demonstrate what I mean:
using System;
class Test
{
public static T Foo<T>(object x)
{
return default(T);
}
public static void Main(string[] args)
{
object a = new object();
dynamic b = a;
var c = Foo<string>(a);
var d = Foo<string>(b);
Console.WriteLine(c.SomeRandomMember); // Invalid
Console.WriteLine(d.SomeRandomMember); // Valid
}
}
The invalid statement is invalid because the type of c
is string
- but the subsequent statement is fine, because the type of d
is dynamic
.
Even though there's only one possible method which it could be bound to at execution time - and even though that binding will always work - the basic rule is that almost any expression involving a dynamic
value is deemed to be of type dynamic
. (There are a few exceptions such as as
and new
.)
To make your return value non-dynamic, just cast your values to object
:
var obj = RedditDynamicObjectWrapperFactory.GetWrapper<LinkWrapper>
((object) child1.kind, (object) child1);
That will now be a statically bound call. Or you could leave it as a dynamically bound call, and use the implicit conversion from an expression of type dynamic
to other types:
LinkWrapper obj = RedditDynamicObjectWrapperFactory.GetWrapper<LinkWrapper>(child1.kind, child1);
Why does c# compiler allow incorrect match of return value type and variable type?
Any expression that has an operand of type dynamic will have a type of dynamic itself.
Thus your expression OneMethod(a)
returns an object that's typed dynamically
so the first part of your code is equivalent to
static void Main()
{
dynamic a = 1;
dynamic temp = OneMethod(a);
int b = temp;
}
one way of argue why this is sensible even in your case depends on whether or not you think the compiler should change behavior for that particular line depending when you add the below method
private static T OneMethod<T>(T number)
Now the compiler won't know the type returned until runtime. It won't even know which method is called. The generic or the non generic. Wouldn't you be surprised if it in the first case marked the assignment as a compile error and then by adding a completely different method it got moved to a runtime error?
Why do I have to explicitly type a local variable?
From Microsoft Docs:
The result of most dynamic operations is itself dynamic.
I believe this is because the types are resolved at runtime (hence "dynamic" typing).
Related:
- Why does a method invocation expression have type dynamic even when there is only one possible return type?
- Why the deduced return type of a method with a ExpandoObject parameter is always dynamic?
- C# intellisense incorrect for method that takes a dynamic argument
Edit: I believe this case is covered by ECMA-334 C# Language Specification 5th Edition (December 2017), Section 12.7.6 Invocation expressions, subsection 12.7.6.1 General, which states that:
An invocation-expression is dynamically bound (§12.3.3) 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
In this case, the compiler classifies the invocation-expression as a value of type dynamic.
Related Topics
Displaying Standard Datatables in MVC
C# How to Test a File Is a Jpeg
Observablecollection and Threading
Azure Asp .Net Webapp the Request Timed Out
Meanings of Declaring, Instantiating, Initializing and Assigning an Object
Are These Objects's References on the Stack or on the Heap
Returning in the Middle of a Using Block
Possible to Iterate Backwards Through a Foreach
How to Set Conditional Breakpoints in Visual Studio
Xmlwriter to Write to a String Instead of to a File
Struct Constructor: "Fields Must Be Fully Assigned Before Control Is Returned to the Caller."
Unhandled Exceptions in Backgroundworker
Waiting for Async/Await Inside a Task
How to Receive Plug & Play Device Notifications Without a Windows Form
How to Hide "Chrome Is Being Controlled by Automated Software" Infobar Within Chrome V76