动态参数和 return 类型

Dynamic arguments and return type

抱歉,如果这太明显了,但我无法解决或将正确的搜索词串在一起以找到我正在寻找的答案。

考虑以下代码:

public IEnumerable<T> Query<T>(string command, dynamic param = null)
{
  return Enumerable.Empty<T>();
}

public T FirstOrDefault<T>(string command, dynamic param = null)
{
  return Query<T>(command, param).FirstOrDefault();
}

IEnumerable<T>Linq 扩展方法 FirstOrDefault 可能由于动态参数而未被提取,但我不明白为什么。

该方法的 Intellisense 声明它将在运行时解析,但我不明白为什么 return 值会受到动态参数的影响,因为它只是一个被传递的参数。

谁能把我从痛苦中解救出来?

因为 C# 有方法重载:要调用的正确方法由参数的静态类型决定。 intellisense本来可以聪明点,发现其实只有一个候选人,但显然没那么聪明

动态语言有不错的智能感知实现,例如 PHP(PHP 用于 Visual Studio 的工具)、Python,它们可以处理类似的事情。我想 dynamic 预计会在真正动态的情况下被稀疏地使用,所以 C# intellisense 的作者没有费心去实现如此聪明的分析。

您不能在 dynamic 类型的对象上调用扩展方法。扩展方法是纯粹的编译时构造。在运行时,它会搜索一个名为 FirstOrDefault 的实例方法,找不到,因此会出错。

您的解决方案是在调用 FirstOrDefault 之前将 Query 的结果转换为适当的类型,或者不使用扩展方法语法而是写出:Queryable.FirstOrDefault(Query<T>(...)) 这将告诉运行时绑定程序它正在尝试将 Query 的结果绑定到适当的静态方法,而不是实例方法。

至于为什么任何接受类型 dynamic 参数的方法总是解析为类型 dynamic 的表达式:这只是规范所说的那样。使用 dynamic 的整个 是将方法的绑定推迟到运行时,因此直到运行时才能确定该表达式的值是什么;为了解决这个问题,它需要传播 dynamic 类型。

问题在于,由于您使用 dynamic 调用了方法,因此 return 将是 dynamic,因此 CLR 可以确定要在运行时使用的正确方法重载,这可能导致 return 类型发生变化。

这意味着您不能直接调用 .FirstOrDefault,因为 class 上不存在该方法,它是一个扩展方法(并且看到有 bot IEnumerableIQueryable 形式,一个不明确的扩展)。虽然这很方便,但动态类型不会查找扩展方法,只会查找在运行时 class 上定义的扩展方法。

不过,解决此问题的一种方法是将扩展方法作为静态方法调用:

return Enumerable.FirstOrDefault(Query<T>(command, param));

不太优雅,但由于所有扩展方法都是静态方法,这应该可以解决您的问题。