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

时间:2011-10-31 07:42:12

标签: c# linq generics dynamic type-inference

我最近一直在玩Massive,一个返回IEnumerable< dynamic>的集合的微型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<string> strings = new[] { "a", "b", "c" };
IEnumerable<dynamic> dynamics = strings;

IEnumerable<string> output = dynamics.Select(d => string.Format("string is {0}", d)); // compiler error on this line

编译器抱怨:

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

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

通过明确指定TSource和TResult很容易解决:

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

或者我可以将结果分配给IEnumerable&lt; dynamic&gt; ...

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

我还确认,当我替换我的IEnumerable&lt; dynamic&gt;时,不会发生此问题。使用IEnumerable&lt; object&gt;:

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

有趣的是,即使是以下工作:

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

有人可以解释这里发生了什么吗?这是C#编译器的已知限制还是我发现了另一个错误?

2 个答案:

答案 0 :(得分:7)

你需要:

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

它无法推断返回类型为string,因为dynamic表示必须假设返回值为dynamic,以防string.Format更合适的重载dynamic 1}}用于提供的特定类型(具有不同的返回类型)。即使我们另有说明,string的规范也不同意我们; p通过向dynamic添加显式强制转换,我们会使返回类型清晰。

我个人认为object没有用;你不妨使用IEnumerable<string> strings = new[] { "a", "b", "c" }; IEnumerable<object> dynamics = strings; IEnumerable<string> output = dynamics.Select(d => string.Format( "string is {0}", d)); ,那么首先不是问题:

IEnumerable<string>

(或者确实,保留为dynamic)我假设您有其他理由使用在此示例中不可见的{{1}}。

答案 1 :(得分:0)

我认为这个问题与动态无关。我希望通常会发生'用户期望失败'。选择&lt;&gt;将推断泛型类型参数 1

可以解决它:

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

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