Why Does a Method Invocation Expression Have Type Dynamic Even When There Is Only One Possible Return Type

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



Leave a reply



Submit