5

I (lazily) used var in the original version of the below code and got a strange runtime exception in an entirely different part of the code. Changing "var" to "int" fixed the runtime exception but I cannot quite see why. I boiled the code down to this example;

public class Program
{
    private static List<string> Test(string i) { return new List<string> {i}; }
    private static dynamic GetD() { return 1; }

    public static void Main()
    {
        int value1 = GetD();   // <-- int
        var result1 = Test("Value " + value1);
        // No problem, prints "Value 1", First() on List<string> works ok.
        Console.WriteLine(result1.First());

        var value2 = GetD();   // <-- var
        var result2 = Test("Value " + value2);
        // The below line gives RuntimeBinderException 
        // 'System.Collections.Generic.List<string>' does not contain a 
        // definition for 'First'
        Console.WriteLine(result2.First());
    }
}

I can see the type of the "var" being dynamic instead of int, but why does that type propagate to and affect the behaviour of the return value of the call to Test()?

EDIT: Maybe I should clarify my question; I can see that dynamic propagates to result2, what I cannot understand is why, when the IDE clearly indicates that List<string> Test(string) is the method called, it still infers the return value as dynamic. Is it a case of the IDE being more clever than the compiler?

Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294
  • Your IDE gives you explicitly the method called? Not mine (Visual Stduio 2010 gives (dynamic expression)). What is your IDE? – Cédric Bignon Jan 26 '13 at 10:31
  • @CédricBignon Visual Studio 2012 with ReSharper, clicking on the call and using "go to definition" will jump to the method. Also, if I change the call to "Test2", it will mark the call as an error, telling me there's no such method to call. – Joachim Isaksson Jan 26 '13 at 10:36
  • Your IDE is not so clever. Try to add the new method: _private static List Test(int i) { return new List { i }; }_ It will propose you the two possibilities. – Cédric Bignon Jan 26 '13 at 10:43
  • possible duplicate of [Extension method and dynamic object in c#](http://stackoverflow.com/questions/5311465/extension-method-and-dynamic-object-in-c-sharp) – nawfal Jul 19 '14 at 21:03

3 Answers3

2

The problem is that First is an extension method not an instance method and runtime binder has trouble differentiating extension methods from instance methods dynamically.

you can read more on this here:

Extension method and dynamic object

Community
  • 1
  • 1
TKharaishvili
  • 1,997
  • 1
  • 19
  • 30
2

Your code is compiled like this:

public static void Main()
{
    int value1 = GetD();   // <-- int
    List<string> result1 = Test("Value " + value1);
    // No problem, prints "Value 1", First() on List<string> works ok.
    Console.WriteLine(result1.First());

    dynamic value2 = GetD();   // <-- var
    dynamic result2 = Test("Value " + value2);
    // The below line gives RuntimeBinderException 
    // 'System.Collections.Generic.List<string>' does not contain a 
    // definition for 'First'
    Console.WriteLine(result2.First());
}

result2 is a dynamic object, then extention method is not supported on it (used as extention method).

However, you can do this:

Console.WriteLine(Enumerable.First(result2));

UPDATE

Your IDE is not so clever. Try to add the new method:

private static List<int> Test(int i) { return new List<int> { i }; }

It will propose you the two possibilities.

UPDATE2

Paragraph 7.6.5 of the C# specification:

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.
Cédric Bignon
  • 12,892
  • 3
  • 39
  • 51
  • `var result2 = Test("Value " + value2);` will be compiled as `List result2 = Test("Value " + value2);` not `dynamic result2 = Test("Value " + value2);`. – Hamlet Hakobyan Jan 26 '13 at 10:25
  • Nope, as _value2_ is a dynamic object, the compiler can not define which method _Test_ will be used, then, it can not determine its return value type. – Cédric Bignon Jan 26 '13 at 10:28
  • We have only one `Test` method with return type `List`. – Hamlet Hakobyan Jan 26 '13 at 10:31
  • Yes, that's right, but the compiler still "prefer" to consider it in as a dynamic expression. – Cédric Bignon Jan 26 '13 at 10:36
  • OK, i understand that, but why? Any link to specification. – Hamlet Hakobyan Jan 26 '13 at 10:37
  • @CédricBignon So you're saying the compiler prefers to use `dynamic` even if it knows the method called and thus the type, just to not change the behaviour in case another overload is added later? Sort of makes sense, but also somewhat confuses me regarding `var` that usually goes for the most specific type info it can deduce at compile time. I think I need to deep dive into the specification for this one, `dynamic` isn't intuitive to me obviously :) – Joachim Isaksson Jan 26 '13 at 10:53
  • I've updated my post with the reference to the C# specifications. – Cédric Bignon Jan 26 '13 at 11:02
0

You can see the below iamge shows obviously what could be the problem.

GetType() Value

The GetType() of result2 that shows it was dynamic object.

Also, dynamic keyword doesn't provide support for extension methods

Smaug
  • 2,625
  • 5
  • 28
  • 44