扩展方法和动态对象

103

我将我的问题总结成以下代码片段。

List<int> list = new List<int>() { 5, 56, 2, 4, 63, 2 };
Console.WriteLine(list.First());

上述代码运行良好。

现在我尝试了下面的代码:

dynamic dList = list;
 Console.WriteLine(dList.First());

但是我得到了 RuntimeBinderException 异常。为什么会这样?


这似乎是刚刚4天前提出的这个问题的重复:https://dev59.com/1W435IYBdhLWcg3wrSPN - jbtule
@jbtule 的区别在于 this 在这里是动态的,但如果你来到这里,你可能也应该看看那个问题。 - nik.shornikov
3个回答

150

对Jon的回答进行补充,这个问题不起作用的原因是因为在常规的非动态代码中,扩展方法通过对编译器已知的所有类进行全面搜索来工作,以查找具有匹配的扩展方法的静态类。搜索按照命名空间嵌套和每个命名空间中可用的using指令的顺序进行。

这意味着为了正确解析动态扩展方法调用,DLR必须在运行时以某种方式知道您源代码中的所有命名空间嵌套和using指令。我们没有一种机制可以将所有这些信息编码到调用站点中。我们考虑过发明这样的机制,但决定它成本太高,产生的进度风险也不值得。


3
这个特性即将推出吗?这肯定会是一个重大变化;目前导致RunTimeBinderExceptions错误的调用会在重新编译源代码后突然开始工作。此外,实施这种特性是否会有任何安全风险? - Ani
5
@ani:我们计划实施那个功能吗?不。有任何安全风险吗?我不知道有什么安全风险,请先说一下攻击者是谁,以及他们对用户造成了什么威胁。 - Eric Lippert
@EricLippert,我已经明白了所有的dynamic对象都等同于C#中的DynamicObject,所以没有办法区分它们,这也是为什么不能向dynamic添加扩展方法的原因之一,对吗? - Tomas Ramirez Sarduy
@EricLippert考虑扩展一下这个答案,并添加类似于“当任何参数是动态的时,所有解析都被推迟到运行时”的句子。虽然对您来说很明显,但在SO上很难找到其他地方的重要信息(例如,请参见https://dev59.com/fajja4cB1Zd3GeqP9FMz)。 - Alexei Levenkov
我知道有足够的解释(而且我本可以自己找到答案:)),我的担心是其他人不太可能将“作为动态传递的一个参数”与整个调用在那一点变成运行时之间建立联系。如果我不知道答案,我不知道如何从“C#扩展动态”或“C#扩展C1973”得出这个答案和你链接的答案。 - Alexei Levenkov
显示剩余2条评论

142

在扩展Stecya的答案上... 扩展方法不支持以扩展方法的形式进行动态类型化,也就是说不能像调用实例方法一样去调用扩展方法。然而,这种方式可以正常工作:

dynamic dList = list;
Console.WriteLine(Enumerable.First(dList));
当然,那可能有用,如果你能提供更多关于为什么和如何使用动态类型的信息,我们或许可以提供更多帮助。

我正在使用动态对象进行编程,但是遇到了异常。你是否写过任何关于这个主题的文章,讲述何时使用或不使用动态对象? - santosh singh
20
@geek:就我个人而言,我的经验法则是只有在确实需要使用dynamic时才使用它……基本上,如果您否则将使用反射来访问成员,那么这是一个很大的提示。另一方面,我是一个顽固的静态类型支持者-其他人可能会建议不那么悲观的策略 :) - Jon Skeet
2
把类型强制转换回已知类型可能更易读,可以这样实现:Console.WriteLine(((List<int>)dList).First()); 或者 Console.WriteLine((dList as List<int>).First());。 - AVee

20

因为First()不是List的一个方法,它是在Linq扩展中定义的,用于IEnumerable<>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接