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

时间:2014-06-17 04:28:28

标签: c# generics dynamic

我正在尝试编写一个接受任何IEnumerable<T>作为参数的方法;并从代码中调用它,直到运行时才会知道T

我可以将它作为泛型方法编写,然后在我想使用时用反射调用它,但出于各种原因我决定使用类型动态。但是,当我从实体框架函数import(a System.Data.Objects.ObjectQuery<T>)传入强类型集合输出并尝试访问Count()方法时,它引发了System.Data.Objects.ObjectQuery<T>没有的效果的异常有一个Count()方法,但情况并非如此。

我通过将与对象的交互限制为foreach语句来解决这个问题,从而使编译器弄清楚如何枚举它,所以这不是问题。我更感兴趣的是理解为什么我不能这样做。我的猜测是,Count()是基类或接口上的方法,因此由于某种原因无法访问,或者编译器通常在编译期间将对Count()的调用转换为Count<T>而不能在这种情况下这样做?帮助赞赏。

编辑:我觉得编译器可能没有将非通用Count()转换为通用Count<T>。当您在Count()上调用IEnumerable<T>.Count()时,实际上正在调用Count<T>(),这只是编译器负责的语法糖。我认为它可能不会在运行时将Count()转换为Count<T>()。很高兴得到对此的确认,因为如果是这样,这对于使用类型动态的泛型来说是一个非常烦人的限制。

还编辑添加代码:

  

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException未被用户代码处理HResult = -2146233088   Message ='System.Data.Objects.ObjectResult'不包含'Count'的定义   Source =匿名托管DynamicMethods程序集   堆栈跟踪:   在CallSite.Target(Closure,CallSite,Object)   在System.Dynamic.UpdateDelegates.UpdateAndExecute1 [T0,TRet](CallSite站点,T0 arg0)   at [snip]中的MyNamespace.HtmlHelpers.RenderTable(Object list,String id,String cssClass)

抛出异常的代码示例:

public static MvcHtmlString RenderTable(dynamic list, string id, string cssClass)
{
    int x = list.Count();
    ........
    ........
}

2 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

这个小程序可以证明这个问题:

using System;

namespace SO24255725
{
    public static class Extension
    {
        public static int Count(this Program a)
        {
            return 42;
        }
    }


    public class Program
    {
        static void Main()
        {
            Program test = new Program();
            PrintCount(test);
        }

        static void PrintCount(dynamic data)
        {
            Console.WriteLine(data.Count());
        }
    }
}

原因如下:

  

动态功能无法在运行时任意绑定到扩展名   方法,因为扩展方法功能被定义为作用域   仅限于使用编译单元的#。在运行时,使用&#39;   信息消失了,所以运行时绑定程序无法解析   扩展方法。这是设计......

来源:http://connect.microsoft.com/VisualStudio/feedback/details/771364/use-of-extension-methods-using-a-dynamic-variable-doesnt-work

您没有列出&#34;各种原因&#34;为什么你决定使用动态。可能你不应该。