我正在尝试编写一个接受任何 IEnumerable<T>
作为参数的方法;并从 T
的代码中调用它直到运行时才会知道。
我可以将它写成一个通用方法,然后在我想使用它时通过反射调用它,但出于各种原因我决定使用动态类型。但是,当我从 Entity Framework 函数导入(System.Data.Objects.ObjectQuery<T>
)传入一个强类型集合输出并尝试访问 Count() 方法时,它抛出了一个异常 System.Data.Objects.ObjectQuery<T>
没有 Count() 方法,但事实并非如此。
我通过将我与对象的交互限制在 foreach 语句中来解决这个问题,从而让编译器弄清楚如何枚举它,所以这不是问题。我更有兴趣了解为什么我不能这样做。我的猜测是,要么 Count() 是基类或接口(interface)上的方法,因此由于某种原因无法访问,或者编译器通常将对 Count() 的调用转换为 Count<T>
在编译期间,在这种情况下不能这样做?感谢帮助。
编辑:我觉得可能是编译器没有翻译非通用 Count()
的情况。进入通用 Count<T>
.当您调用 Count()
在 IEnumerable<T>.Count()
上你实际上是在打电话 Count<T>()
,它只是编译器处理的语法糖。我认为它可能不会对 Count()
进行相同的翻译至 Count<T>()
在运行时。很高兴得到这一点的确认,因为如果是这样的话,这是使用动态类型的泛型的一个非常烦人的限制。
还编辑添加代码:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was unhandled by user code HResult=-2146233088 Message='System.Data.Objects.ObjectResult' does not contain a definition for 'Count' Source=Anonymously Hosted DynamicMethods Assembly StackTrace: at CallSite.Target(Closure , CallSite , Object ) at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0) at MyNamespace.HtmlHelpers.RenderTable(Object list, String id, String cssClass) in [snip]
以及抛出异常的代码示例:
public static MvcHtmlString RenderTable(dynamic list, string id, string cssClass)
{
int x = list.Count();
........
........
}
最佳答案
原因list.Count()
list
时给出运行时错误是 dynamic
是那个Count
是一种扩展方法 - Enumerable.Count<T>
- 扩展方法只能在编译时发现。 (如果我对此有误,请有人纠正我 - 这是我的理解。)
在直接回答您的问题之前,请看一下这个小玩具程序。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Example
{
class Program
{
static void Main(string[] args)
{
IEnumerable<int> a = Enumerable.Range(1, 3);
IEnumerable b = a;
IEnumerable<double> c = b.SelectTimesPi();
foreach (var x in c)
{
Console.WriteLine(x);
}
Console.ReadLine();
}
}
static class Extensions
{
public static IEnumerable<double> SelectTimesPi(this IEnumerable source)
{
return source
.Cast<dynamic>()
.Select(x => Math.PI * x)
.Cast<double>();
}
}
}
它的输出如下。
3.14159265358979
6.28318530717959
9.42477796076938
SelectTimesPi
扩展方法采用任何 IEnumerable<T>
其中 T
仅在运行时已知,然后将其元素操作为 dynamic
值。 source
可能是 IEnumerable<byte>
, IEnumerable<short>
, IEnumerable<float>
等
你当然可以用非扩展方法做同样的事情,就像你问的那样。我只是以此为例,因为看起来您的目标是最终创建一个扩展方法。
诀窍在于使用 Enumerable.Cast<dynamic>
在 IEnumerable
上.
回到你原来的问题。您所要做的就是制作您的 RenderTable
方法采取 list
作为IEnumerable
而不是 dynamic
, 然后使用 Enumerable.Cast<dynamic>
.
public static MvcHtmlString RenderTable(IEnumerable list, string id, string cssClass)
{
IEnumerable<dynamic> dynamicList = list.Cast<dynamic>();
int x = dynamicList.Count();
...
}
现在调用Count
不是动态调度,因此扩展方法可以正常工作。
总结:
IEnumerable<int> x = ...;
(x as IEnumerable).Cast<dynamic>().Count(); // does work
(x as dynamic).Count(); // does not work
关于c# - 调用动态类型对象的泛型方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24255725/