c# - 调用动态类型对象的泛型方法

标签 c# generics dynamic

我正在尝试编写一个接受任何 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/

相关文章:

sql - 如何分隔字符串并转换为列标题?

c# - 当目标是 ImageBrush.ImageSource 时,TemplateBinding 失败

c# - 尝试读取或写入 protected 内存。这通常表明其他内存已损坏

c# - 存储派生对象的泛型类列表

c# - Autofac:在泛型方法中解析泛型接口(interface)

dynamic - Dart 2中的动态类型可以分配给差异类型的多个值吗?

javascript - 如何在ui-selectable jquery中动态更改选项列表

C# 位图 - 找不到如何删除 OutOfMemoryException

c# - Unity - 如果场景名称以

generics - 将字节数组反序列化为名称为 String 的类