我最近一直在玩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#编译器的已知限制还是我发现了另一个错误?
答案 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 当我有时间的时候,我会尝试添加一个恰恰是这种“令人惊讶的模糊”案例的例子 子>