Общий метод, назначенный делегату
Я немного озадачен делегатами и универсальными методами.
Можно ли назначить делегат методу с параметром общего типа?
то есть:
//This doesn't allow me to pass a generic parameter with the delegate.
public delegate void GenericDelegate<T>()
someDelegate = GenericMethod;
public void GenericMethod<T>() where T : ISomeClass
{
}
Я пытаюсь передать этот делегат в функцию с общим типом интерфейса, который ожидает этот метод, с такой функцией:
void CheckDelegate(GenericDelegate<ISomeClass> mechanism);
чтобы я мог использовать делегат следующим образом:
someDelegate<ImplementsSomeClass>();
Ответы
Ответ 1
Ваш вопрос не имеет смысла, потому что вы никогда не сможете использовать открытый общий тип для объявления места хранения (например, локальной переменной или поля). Он всегда должен быть закрыт.
Я понимаю, что вы хотите передать GenericDelegate<T>
методу, принимающему такое значение как аргумент. Но даже тогда тип делегата становится закрытым с T
в качестве параметра типового типа.
В вашем примере кода вы пишете
someDelegate = GenericMethod;
но какой тип someDelegate
должен иметь? Он должен быть либо явно закрытым (GenericDelegate<string>
), либо закрыт параметром общего типа из внешней области:
void SomeOuterMethod<T>() where T : ISomeClass {
GenericDelegate<T> someDelegate = GenericMethod<T>;
}
Надеюсь, я понял вашу проблему. Если нет, уточните пожалуйста. Если вы немного придумаете , что хотите выполнить, я попытаюсь предложить практическое решение.
Другие языки, такие как Haskell, имеют поддержку для передачи значений открытых общих типов (другими словами, вы можете иметь переменную типа IEnumerable<>
). Это необходимо для реализации монадов. CLR не имеет этой функции.
Новая мысль: вместо делегата вы можете создать базовый базовый тип с общим методом, который можно переопределить:
interface CheckHandler {
public void Check<T>(T someArg);
}
Надеюсь, что это ваш сценарий. Вы не можете свободно передавать любой CheckHandler
. Его метод Check
может быть вызван с аргументом произвольного типа.
Ответ 2
Возможно иметь единственную "вещь", которая может работать с несколькими типами параметров, но класс Delegate
не подходит для этого. Вместо этого вам необходимо определить интерфейс. В качестве простого примера:
public interface IMunger<TConstraint>
{
void Munge<T>(ref T it) where T : TConstraint;
}
public class Cloner : IMunger<ICloneable>
{
public void Munge<T>(ref T it) where T : ICloneable
{
if (typeof(T).IsValueType) // See text
return;
it = (T)(it.Clone());
}
}
Даже если система имела предопределенный тип делегата с параметром by-ref (так что, например, ActByRef<ICloneable>
имела бы подпись void Invoke(ref ICloneable p1)
), такой делегат будет использоваться только для переменной точного типа ICloneable
. В отличие от этого, один объект класса нестандартного типа Cloner
может предоставить метод, подходящий для использования с любым типом местоположения хранилища, который реализует ICloneable
. Заметим также, что если метод передан a ref
переменной, содержащей ссылку на экземпляр типа boxed value-type, он заменит его ссылкой на копию экземпляра, но если он передан a ref
to переменную value-type, она оставит ее как есть (если значение-тип не сохраняет свое состояние в объекте mutable class, к которому он содержит ссылку, - очень изворотливый шаблон, говорящий StructType foo = (StructType)(bar.Clone());
, будет эквивалентен просто foo = bar
, тип структуры может захотеть реализовать ICloneable
, чтобы позволить ему участвовать в иерархии глубокого клонирования, но это не значит, что его метод Clone
должен что-то делать.
Ответ 3
Обновлен пример поддержки метода как параметра, который просто показывает, как вызвать общий делегат как параметр другого метода.
class Program
{
public delegate T Transformer<T>(T arg) where T : IComparable;
public static void Transform<T>(T value, Transformer<T> method) where T: IComparable
{
Console.WriteLine(method(value));
}
static void Main(string[] args)
{
Transform(5, Square);
}
static int Square(int x)
{
return x * x;
}
}
Ответ 4
Я попробовал следующее:
public class Test
{
public interface ISomeClass { }
public class ImplementsSomeClass : ISomeClass { }
public delegate void GenericDelegate<T>() where T : ISomeClass;
public void GenericMethod<T>()
{
// EDIT: returns typeof(ImplementsSomeClass)
var t = typeof(T);
}
public void CheckDelegate(GenericDelegate<ISomeClass> mechanism)
{
// EDIT: call without generic argument since it is already determined
mechanism();
}
public void test()
{
GenericDelegate<ISomeClass> someDelegate = GenericMethod<ImplementsSomeClass>;
CheckDelegate(someDelegate);
}
}
И у меня нет ошибок компиляции. Является ли это проблемой времени выполнения или я неправильно понял ваше описание проблемы?
Ответ 5
Вы также можете использовать действие, которое:
Действие точно такое же, как: delegate void... (T t)
class Temp : Interface1
{
}
class Program
{
static void Main(string[] args)
{
Action a = GenericMethod<Temp>;
a();
}
private static void GenericMethod<T>() where T : Interface1
{
Console.WriteLine("Hello World!");
Console.ReadKey();
}
}