为什么编译器推断var是动态的而不是具体的类型?

时间:2017-10-02 14:40:01

标签: c#

鉴于这个简短的示例程序:

    static void Main(string[] args)
    {
        Console.WriteLine(Test("hello world"));
    }

    private static int Test(dynamic value)
    {
        var chars = Chars(value.ToString());
        return chars.Count();
    }

    private static IEnumerable<char> Chars(string str)
    {
        return str.Distinct();
    }

运行时,会产生类似于:

的异常

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: ''object' does not contain a definition for 'Count''

含义编译器选择dynamic作为chars变量的首选类型。

考虑到IEnumerable<char>方法不返回动态,是否有任何理由不选择Chars作为具体类型?只需手动将类型更改为IEnumerable<char>即可解决问题,但我想知道为什么dynamic在这种情况下是默认值?

修改

我可能使用了比必要更复杂的例子。似乎这里提出的问题是:

Anomaly when using 'var' and 'dynamic'

提供更简洁的示例以及一些有关其工作方式的见解。

https://blogs.msdn.microsoft.com/ericlippert/2012/11/05/dynamic-contagion-part-one/

描述编译器如何处理动态。

4 个答案:

答案 0 :(得分:5)

使用dynamic,所有方法调用都在运行时解析。因此,当您调用Chars()Count()甚至ToString()时,它拒绝猜测编译时实际调用的方法。它可能是任何东西,返回任何东西。这通常被称为“动态传染”。

对于所有编译器都知道,somtimes value.ToString()将返回MyRandomPOCOClass,并且在运行时它将能够挖掘一些像Tuple<int,String> Chars(MyRandomPOCOClass x)这样的重载。也许下次value.ToString()会返回int。所有赌注都已关闭。 dynamic将C#变成脚本语言。

以下是动态运行时重载行为(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);
}

输出:

String foo
Int 34

答案 1 :(得分:2)

使用dynamic value,编译器如何知道value.ToString()返回什么?

它与我们熟悉的C#方法同名,但它有所不同。

答案 2 :(得分:0)

因为传递给Test()的参数是动态的,并且该参数被传递给Chars()。对于任何涉及动态类型的语句,动态本质上都会将类型检查推迟到运行时,并且就任何涉及动态变量的语句只能产生动态变量而言,就是病毒式的#34;

答案 3 :(得分:0)

  

使用str.Distinct()时,它返回一个类型的对象       IEnumerable<char>下的字符变量System.Linq.Enumerable DistinctIterator的char类型。   这本身没有Count方法。它是一个迭代器,内部逐个返回值。   将其投放到IEnumerable<char>以获取计数。   你可以循环IEnumerable并打印值(对于下面的循环工作正常,因为它是一个迭代器)但是set的底层返回值没有Count方法,除非迭代器类型的变量被强制转换为IEnumerable<char>。   否则,您可以使用str.Distinct()。ToList(),这将在没有强制转换的情况下正常工作。

static void Main(string[] args)
{
    Console.WriteLine(Test("hello world"));
}

private static int Test(dynamic value)
{
    var chars = Chars(value.ToString());
    //foreach (var c in chars)
    //        Console.WriteLine(c);
    return ((IEnumerable<char>)chars).Count();
}

private static IEnumerable<char> Chars(string str)
{
    return str.Distinct();
}