MethodInfo、CreateDelegate 和通用方法
感谢 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();
}
}
如上所述,我能看到的唯一区别是:
- 不同的返回类型(
T
有效,IEnumerable
无效)[编辑:这不是'是的,第一个版本使用BaseItem
,而不是T
; oops] - 重载(
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:
- Different return type (
T
works,IEnumerable<T>
doesn't) [EDIT: this isn't right, first version usesBaseItem
, notT
; oops] - Overloads (
GetItem
has no overloads,GetList
has several; I only need the delegate toGetList()
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我通过将委托声明为
IEnumerable
来完成这项工作。这允许它创建委托。剩下的就只是基本的选角了。以下更改修复了上面的第二个代码块。然后
我可以执行
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.and
I can then do
myList.Sort()
and all the other stuff I am trying to do in my system at work.在进行一些小的修改以编译第二个示例之后,我能够运行它并且它可以很好地获取并调用委托。
我不确定你是如何编译第二个例子的。这里似乎有问题。
该委托被声明为返回 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.
I'm not sure how you got your second example to compile though. There appears to be a problem here.
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).