Благодаря ответу Джона Скита в этом вопросе у меня работает следующее:
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<T>
не работает) [РЕДАКТИРОВАТЬ: это неправильно, первая версия использует BaseItem
, а не T
; упс]
- Перегрузки (
GetItem
не имеет перегрузок, GetList
имеет несколько; мне нужен только делегат на GetList()
без параметров
Update1: Сэм помог мне определить некоторые проблемы. Если возвращаемый тип делегата является общим (например, IEnumerable<BaseItem>
), он задыхается, когда я пытаюсь поменять местами базовые / производные типы. Можно ли как-нибудь объявить мой метод GetList
, как показано ниже? Мне нужно иметь возможность указать, что T
наследуется от BaseItem
, но если бы я мог, он бы работал нормально для меня.
public static IEnumerable<BaseItem> GetList<T>() where T : class
Другим вариантом будет «обобщение» моего объявления делегата. Все примеры, которые я могу найти, используют общий для параметров, а не тип возвращаемого значения. Как мне это сделать (выдает ошибку компилятора, причина T
не определена и не позволяет использовать ограничение where
):
public delegate IEnumerable<T> GetListDelegate();