MethodInfo、CreateDelegate 和通用方法

发布于 2024-08-17 10:02:38 字数 3114 浏览 3 评论 0原文

感谢 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 无效)[编辑:这不是'是的,第一个版本使用 BaseItem,而不是 T; oops]
  2. 重载(GetItem 没有重载,GetList 有几个重载;我只需要委托给 GetList() 且没有参数

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

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

另一种选择是“泛化”我的委托声明。我可以找到的所有示例都使用泛型作为参数,而不是返回类型(它会抛出异常)。编译器错误导致 T 未定义,并且它不允许我使用 where 约束):

public delegate IEnumerable<T> GetListDelegate();

Thanks to Jon Skeet's answer in this question I have the following working:

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

When I try to apply the same thing to a method with a different return type and overloads (those are the only differences I can come up with), I get an annoying "ArgumentException: Error binding to target method." on the call to CreateDelegate. The below is a working example that gets the error, just copy/paste into a console app.

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();
  }
}

As mentioned above, the only differences I can see are:

  1. Different return type (T works, IEnumerable<T> doesn't) [EDIT: this isn't right, first version uses BaseItem, not T; oops]
  2. Overloads (GetItem has no overloads, GetList has several; I only need the delegate to GetList() with no params

Update1: Sam helped me pinpoint some issues. If the return type of the delegate is generic (e.g. IEnumerable<BaseItem>), it's choking when I try to swap base/derived types around. Is there any way I can declare my GetList method like below? I need to be able to indicate that T inherits from BaseItem, but if I could then it would work fine for me.

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

The other option would be to "genericize" my delegate declaration. All examples I can find use a generic for the params, not the return type. How do I do this (it throws a compiler error cause T is undefined, and it won't let me use the where constraint):

public delegate IEnumerable<T> GetListDelegate();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

终止放荡 2024-08-24 10:02:38

我通过将委托声明为 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() 以及我在工作系统中尝试执行的所有其他操作。

I've gotten this working by declaring the delegate as just IEnumerable. This allows it to create the delegate. All that was remaining then was just basic casting. The below changes fix the second code block above.

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

and

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

I can then do myList.Sort() and all the other stuff I am trying to do in my system at work.

爱,才寂寞 2024-08-24 10:02:38

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

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 来声明协变(或逆变,我不确定是哪一个)。

After making some minor modifications to get the second example to compile, I was able to run it and it gets and calls the delegate fine.

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();
        }
    }
}

I'm not sure how you got your second example to compile though. There appears to be a problem here.

public delegate IEnumerable<BaseItem> GetListDelegate();

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

The delegate is declared as returning IEnumerable but then you call it and assign the result to IEnumerable. This isn't supported in C# 3.5. It is in C# 4 but it would require declaring the BaseItem/DerivedList differently to declare covariance (or contravariance, I'm not sure which).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文