【问题标题】:MethodInfo, CreateDelegate and Generic MethodsMethodInfo、CreateDelegate 和通用方法
【发布时间】:2010-01-14 04:02:29
【问题描述】:

感谢 Jon Skeet 在this 问题中的回答,我有以下工作:

public delegate BaseItem GetItemDelegate(Guid itemID);

public static class Lists
{
  public static GetItemDelegate GetItemDelegateForType(Type derivedType)
  {
    MethodInfo method = typeof(Lists).GetMethod("GetItem");
    method = method.MakeGenericMethod(new Type[] { derivedType });
    return (GetItemDelegate)Delegate.CreateDelegate(typeof(GetItemDelegate), method);
  }

  public static T GetItem<T>(Guid itemID) where T : class { // returns an item of type T ... }
}

public class DerivedItem : BaseItem { }

// I can call it like so:
GetItemDelegate getItem = Lists.GetItemDelegateForType(typeof(DerivedItem));
DerivedItem myItem = getItem(someID); // this works great

当我尝试将相同的东西应用于具有不同返回类型和重载的方法(这些是我能想到的唯一区别)时,我得到一个烦人的“ArgumentException:错误绑定到目标方法”。打电话给CreateDelegate。下面是一个得到错误的工作示例,只需复制/粘贴到控制台应用程序中即可。

public delegate IEnumerable<BaseItem> GetListDelegate();

public class BaseItem { }
public class DerivedItem : BaseItem { }

public static class Lists
{
  public static GetListDelegate GetListDelegateForType(Type itemType)
  {
    MethodInfo method = typeof(Lists).GetMethod("GetList", Type.EmptyTypes); // get the overload with no parameters
    method = method.MakeGenericMethod(new Type[] { itemType });
    return (GetListDelegate)Delegate.CreateDelegate(typeof(GetListDelegate), method);
  }

  // this is the one I want a delegate to, hence the Type.EmptyTypes above
  public static IEnumerable<T> GetList<T>() where T : class { return new List<T>(0); }
  // not the one I want a delegate to; included for illustration
  public static IEnumerable<T> GetList<T>(int param) where T : class { return new List<T>(0); }

  public static Type GetItemType()
  { // this could return any type derived from BaseItem
    return typeof(DerivedItem);
  }
}

class Program
{
  static void Main(string[] args)
  {
    Type itemType = Lists.GetItemType();
    GetListDelegate getList = Lists.GetListDelegateForType(itemType);
    IEnumerable<BaseItem> myList = (IEnumerable<BaseItem>)getList();
  }
}

如上所述,我能看到的唯一区别是:

  1. 不同的返回类型(T 有效,IEnumerable&lt;T&gt; 无效)[编辑:这是不对的,第一个版本使用 BaseItem,而不是 T;哎呀]
  2. 重载(GetItem 没有重载,GetList 有几个;我只需要 GetList() 的委托,没有参数

更新 1:Sam 帮我查明了一些问题。如果委托的返回类型是通用的(例如IEnumerable&lt;BaseItem&gt;),那么当我尝试交换基类/派生类型时,它会令人窒息。有什么方法可以声明我的GetList 方法,如下所示?我需要能够表明T 继承自BaseItem,但如果可以的话,它对我来说就可以了。

public static IEnumerable<BaseItem> GetList<T>() where T : class

另一种选择是“泛化”我的委托声明。我能找到的所有示例都使用泛型作为参数,而不是返回类型。我该怎么做(它会引发编译器错误,因为 T 未定义,它不会让我使用 where 约束):

public delegate IEnumerable<T> GetListDelegate();

【问题讨论】:

  • 在我发布此之前所做的所有阅读中,我知道类型不匹配是导致此异常的一个常见原因。在上面的每个示例中,我在返回调用上都设置了一个断点,并且每种情况下变量方法的类型似乎几乎相同。我看不出有什么不妥。只是想我会添加那个花絮。
  • 为什么这变成了社区维基?
  • 哇,你自己的帖子编辑得够多了,它就自动变成了维基?这真是太愚蠢了。

标签: c# generics delegates methodinfo


【解决方案1】:

我通过将代理声明为 IEnumerable 来完成这项工作。这允许它创建委托。剩下的只是基本的铸造。以下更改修复了上面的第二个代码块。

// declare this as non-generic
public delegate IEnumerable GetListDelegate();

// do some cast-fu to get the list into a workable form
List<BaseItem> myList = getList().Cast<BaseItem>().ToList();

然后我可以在工作中做myList.Sort() 以及我试图在我的系统中做的所有其他事情。

【讨论】:

    【解决方案2】:

    在对第二个示例进行了一些小的修改以编译后,我能够运行它,它可以很好地获取和调用委托。

    using System;
    using System.Collections.Generic;
    using System.Reflection;
    
    namespace ConsoleApplication1
    {
        public delegate IEnumerable<BaseItem> GetListDelegate();
    
        public class BaseItem { }
        public class DerivedItem : BaseItem { }
    
        public static class Lists
        {
            public static GetListDelegate GetListDelegateForType(Type derivedType)
            {
                MethodInfo method = typeof(Lists).GetMethod("GetList", Type.EmptyTypes); // get the overload with no parameters
                method = method.MakeGenericMethod(new[] { derivedType });
                return (GetListDelegate)Delegate.CreateDelegate(typeof(GetListDelegate), method); // *** this throws an exception ***
            }
    
            // this is the one I want a delegate to, hence the Type.EmptyTypes above
            public static IEnumerable<T> GetList<T>() where T : class
            {// returns a collection of T items ... 
                return new T[0];
            }
    
            // not the one I want a delegate to; included for illustration, maybe my different GetMethod() is my problem?
            public static IEnumerable<T> GetList<T>(int param) where T : class
            { // returns a collection of T items ... 
                return new T[0];
            }
        }
    
        public class GenericDelegate
        {
            public static void Test()
            {
    
                // I would call it like so, but first line gets exception, where indicated above
                GetListDelegate getList = Lists.GetListDelegateForType(typeof(BaseItem));
                IEnumerable<BaseItem> myList = getList();
            }
        }
    }
    

    我不确定您是如何编译第二个示例的。这里似乎有问题。

    public delegate IEnumerable<BaseItem> GetListDelegate();
    
    GetListDelegate getList = Lists.GetListDelegateForType(typeof(DerivedList));
    IEnumerable<DerivedList> myList = getList();
    

    委托被声明为返回 IEnumerable,但随后您调用它并将结果分配给 IEnumerable。这在 C# 3.5 中不受支持。它在 C# 4 中,但需要以不同方式声明 BaseItem/DerivedList 以声明协变(或逆变,我不确定是哪个)。

    【讨论】:

    • 我在工作中从我的大型系统中提取了所有这些代码,并更改了类型名称和其他内容以制作可发布的示例。我应该先制作一个示例应用程序并使用它。将 DerivedList 类型更改为 DerivedItem。这是一个复制/粘贴/替换错误。我已经编辑了我的问题来解决这个问题。考虑到这一点,你能否重述你的最后一段,因为我没有关注你。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-12
    • 1970-01-01
    • 2015-11-22
    • 2021-05-15
    相关资源
    最近更新 更多