Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Odd behaviour change with var, dynamic and linq combination

Tags:

c#

var

dynamic

linq

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?

like image 909
Joachim Isaksson Avatar asked Oct 06 '22 05:10

Joachim Isaksson


1 Answers

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.
like image 181
Cédric Bignon Avatar answered Oct 10 '22 02:10

Cédric Bignon