简体   繁体   中英

Odd behaviour change with var, dynamic and linq combination

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?

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

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.

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

GetType()值

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM