Generic Method assigned to Delegate

2019-02-04 16:58发布

问题:

I've been a little puzzled with Delegates and Generic Methods.

Is it possible to assign a delegate to a method with a generic type parameter?

I.E:

//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
{

}

I'm trying to pass this delegate into the function with a generic type of the interface that the method is expecting, with a function like this:

void CheckDelegate(GenericDelegate<ISomeClass> mechanism);

so that I can use the delegate like so:

someDelegate<ImplementsSomeClass>();

回答1:

Your question makes no sense because you can't ever use an open generic type to declare a storage location (like a local variable or field). It must always be closed.

I understand you want to pass a GenericDelegate<T> to a method taking such a value as an argument. But even then the delegate type becomes closed with T as the generic type parameter.

In your sample code you write

someDelegate = GenericMethod;

but what type is someDelegate supposed to have? It must either be obviously closed (GenericDelegate<string>) or closed with a generic type parameter from the outer scope:

void SomeOuterMethod<T>() where T : ISomeClass {
    GenericDelegate<T> someDelegate = GenericMethod<T>;
}

I hope I understood your problem. If not, please clarify. If you elaborate a little on what you want to accomplish I'll try to suggest a practical solution.

Other languages like Haskell do have support for passing around values of open generic types (in other words, you can have a variable of type IEnumerable<>). This is required to implement monads. The CLR does not have that feature.


New thought: instead of a delegate you could create a non-generic base type with a generic method that can be overridden:

interface CheckHandler {
 public void Check<T>(T someArg);
}

Hope that covers your scenario. You can not freely pass any CheckHandler around. Its Check method can then be called with an arbitrary type argument.



回答2:

It is possible to have a single "thing" which can operate upon multiple parameter types, but the Delegate class is not suitable for that. Instead, you'll need to define an interface. As a simple example:

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

Even if the system had a pre-defined delegate type with a by-ref parameter (so that e.g. ActByRef<ICloneable> would have signature void Invoke(ref ICloneable p1)) such a delegate only be used on a variable of exact type ICloneable. By contrast, a single object of non-generic class type Cloner is able to provide a method suitable for use with any storage location type which implements ICloneable. Note also that if the method is passed a ref to a variable holding a reference to a boxed value-type instance, it will replace it with a reference to a copy of the instance, but if it is passed a ref to a value-type variable, it leave it as is (unless the value-type holds its state in a mutable class object to which it holds a reference--a very dodgy pattern--saying StructType foo = (StructType)(bar.Clone()); would be equivalent to just foo = bar; the structure type may want to implement ICloneable so to allow it to take part in a deep-cloning hierarchy, but that doesn't mean its Clone method needs to do anything.



回答3:

Updated the example to support method as parameter which is just demonstating how to call generic delegate as parameter of other method.

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:

I tried the following:

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

And I have no compilation errors. Is it a runtime problem or did I misunderstand your problem description?