动态参数和 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 IEnumerable
和 IQueryable
形式,一个不明确的扩展)。虽然这很方便,但动态类型不会查找扩展方法,只会查找在运行时 class 上定义的扩展方法。
不过,解决此问题的一种方法是将扩展方法作为静态方法调用:
return Enumerable.FirstOrDefault(Query<T>(command, param));
不太优雅,但由于所有扩展方法都是静态方法,这应该可以解决您的问题。
抱歉,如果这太明显了,但我无法解决或将正确的搜索词串在一起以找到我正在寻找的答案。
考虑以下代码:
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 IEnumerable
和 IQueryable
形式,一个不明确的扩展)。虽然这很方便,但动态类型不会查找扩展方法,只会查找在运行时 class 上定义的扩展方法。
不过,解决此问题的一种方法是将扩展方法作为静态方法调用:
return Enumerable.FirstOrDefault(Query<T>(command, param));
不太优雅,但由于所有扩展方法都是静态方法,这应该可以解决您的问题。