【问题标题】:Why does compiler infer var to be dynamic instead of concrete type? [duplicate]为什么编译器将 var 推断为动态而不是具体类型? [复制]
【发布时间】:2017-10-02 14:40:00
【问题描述】:

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

    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 变量的首选类型。

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

编辑

我可能使用了比必要的更复杂的示例。看来这里问的问题:

Anomaly when using 'var' and 'dynamic'

提供了更简洁的示例和一些关于它为何以这种方式工作的见解。

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

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

【问题讨论】:

  • 我认为这与 value 成为 dynamic 有关,将其更改为 string 无需修改 Chars 即可解决问题。我不知道为什么,我也很想知道。
  • 使用dynamic 参数,在运行时解决重载。因此,当您调用Chars() 时,它拒绝在编译时猜测实际调用的是什么方法。
  • 离开.ToString() 会发生什么?
  • 编译器对 ToString() 一无所知。您也可以使用 ToFoo() ,它仍然可以编译。所以一切都被动态绑定,甚至是 Chars() 调用。但是 Count() 是一个扩展方法,DLR 不会找到它们。最简单的解决方法是使用(string)value.ToString() 帮助编译器正确处理。
  • @HenkHolterman 它会失败并出现同样的异常。

标签: c#


【解决方案1】:

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

就编译器所知,有时value.ToString() 将返回MyRandomPOCOClass,并且在运行时它将能够挖掘出一些像Tuple&lt;int,String&gt; 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

【讨论】:

  • 另外,在运行时,很可能 value.Tostring() 在这种情况下确实会被解析为 string.ToString() ,它具有“return this”的逻辑实现,因为@ 987654322@ 说...“这个方法只是简单地返回当前字符串不变”,净效应,动态输入 = 动态输出。
  • 谢谢,第一段很好地解释了这个问题,它为什么会这样工作是有道理的。
【解决方案2】:

有了dynamic value,编译器怎么知道value.ToString()返回什么?

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

【讨论】:

  • 不应该抛出编译器错误,因为Chars(dynamic) 不存在吗?我试过了,它不会产生那个错误,让我想知道为什么。
  • @HimBromBeere 如果您希望编译器告诉您方法是否存在,或者您的值的类型之一是否存在问题,请不要打开编译器关闭并告诉它不要尝试使用 dynamic 检查任何相关表达式的类型。
  • @HimBromBeere 它不会在编译时尝试解决重载问题。在运行时,如果参数是int,它将寻找Chars(int)
  • dynamic 的全部意义在于它可以充当任何值的替身。 Chars(whatever) 很好;在运行时将尝试转换为whatever。方法本身必须仍然存在(不能调用Bars)。
  • @HimBromBeere 这是一个例子:dotnetfiddle.net/kuuGQM
【解决方案3】:

因为传递给 Test() 的参数是动态的,并且该参数传递给 Chars()。对于任何涉及动态类型的语句,Dynamic 本质上将类型检查推迟到运行时,这就是“病毒”,因为任何涉及动态变量的语句只能产生一个动态变量。

【讨论】:

    【解决方案4】:

    当你使用 str.Distinct() 时,它返回一个对象类型 将 char 类型的 System.Linq.Enumerable DistinctIterator 添加到 IEnumerable&lt;char&gt; 下的 chars 变量中。 那本身没有 Count 方法。它是一个迭代器,内部一一返回值。 将其投射到IEnumerable&lt;char&gt; 以获取计数。 您可以遍历 IEnumerable 并打印值(下面的 for 循环可以正常工作,因为它是一个迭代器),但集合中的底层返回值没有 Count 方法,除非将迭代器类型的变量强制转换为 IEnumerable&lt;char&gt;。 否则,您可以使用 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();
    }
    

    【讨论】:

      猜你喜欢
      • 2019-03-20
      • 2021-03-11
      • 1970-01-01
      • 1970-01-01
      • 2019-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多