通用类型推断无法使用动态?

我最近一直在使用Massive,一个返回IEnumerable 集合的Micro-ORM。

当我尝试使用LINQ查询其中一个集合时,我发现了一个意外的问题。

虽然编译器似乎没有任何问题可以解决这个问题,但是即使传递给它的一个参数被声明为动态,string.Format也会返回一个字符串…

dynamic dynamicString = "d"; // just using a string here for simplicity, same problem occurs with any other type string explicitString = string.Format("string is {0}", dynamicString); // works without issues 

…在以下情况中似乎无法推断出这一事实:

 IEnumerable strings = new[] { "a", "b", "c" }; IEnumerable dynamics = strings; IEnumerable output = dynamics.Select(d => string.Format("string is {0}", d)); // compiler error on this line 

编译器抱怨:

 "Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists (are you missing a cast?)" 

由于编译器应该能够推断出我的lambda表达式返回一个字符串,我原本预计它也会推断出Select的TResult应该是string类型(而不是动态的)。

通过明确指定TSource和TResult可以很容易地修复:

 IEnumerable output2 = dynamics.Select(d => string.Format("string is {0}", d)); // works !!! 

或者我可以将结果分配给IEnumerable …

 IEnumerable output3 = dynamics.Select(d => string.Format("string is {0}", d)); // also works 

我还确认当我用IEnumerable 替换我的IEnumerable 时不会发生这个问题:

 IEnumerable objects = strings; IEnumerable output4 = objects.Select(o => string.Format("string is {0}", o)); // works 

有趣的是,甚至以下工作:

 IEnumerable output5 = dynamics.Select(d => string.Format("string is {0}", (object)d)); // works IEnumerable output6 = dynamics.Select(d => string.Format("string is {0}", (string)d)); // works 

谁能解释一下这里发生了什么? 这是C#编译器的已知限制还是我发现了另一个错误?

你需要:

 IEnumerable output = dynamics.Select(d => (string)string.Format( "string is {0}", d)); 

它不能推断返回类型是string ,因为dynamic意味着它必须假设返回是dynamic ,以防有一个更合适的string.Format重载提供给特定类型(具有不同的返回类型)。 即使我们不知道, dynamic规范也不同意我们; p通过向string添加显式强制转换,我们使返回类型清晰。

就个人而言,我觉得这里没有dynamic ; 你不妨使用object ,那么它首先不是问题:

 IEnumerable strings = new[] { "a", "b", "c" }; IEnumerable dynamics = strings; IEnumerable output = dynamics.Select(d => string.Format( "string is {0}", d)); 

(或者实际上,保留为IEnumerable )我假设您有其他原因使用在此示例中不可见的dynamic

我认为这个问题与动态无关。 当希望.Select <>将推断泛型类型参数时,我经常会发生’用户期望失败’。

可以像这样解决它:

  Func selector = d => string.Format("string is {0}", d); IEnumerable output = dynamics.Select(selector); 

1当我有空的时候,我会尝试添加一个恰恰是这种“令人惊讶的模糊”案例的例子