Общий метод, назначенный делегату

Я немного озадачен делегатами и универсальными методами.

Можно ли назначить делегат методу с параметром общего типа?

то есть:

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