14

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>();
10001110101
  • 420
  • 4
  • 10

4 Answers4

10

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.

usr
  • 168,620
  • 35
  • 240
  • 369
  • I'm attempting to modify the state of an object. The type T will designate the state that the object will be required to transition to. – 10001110101 Dec 10 '13 at 21:21
  • A good explanation why the delegate cannot be called as `someDelegate();`. I totally agree. – helb Dec 10 '13 at 21:25
  • So it's impossible because I'm converting the type to closed when I pass it as correct? – 10001110101 Dec 10 '13 at 21:26
  • 1
    I think so. If you want to keep the genericity, you must add generic type parameters to all methods (or their surrounding types) so that you have the ability to flow the actual generic type argument through all code that is involved. I added a new idea to the answer. Does that help? – usr Dec 10 '13 at 21:32
  • @usr: Sounds like what I was thinking [see my answer] – supercat Dec 10 '13 at 21:46
3

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.

supercat
  • 77,689
  • 9
  • 166
  • 211
2

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;
    }
}
mybrave
  • 1,662
  • 3
  • 20
  • 37
0

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?

helb
  • 7,609
  • 8
  • 36
  • 58
  • I want to call the delegate with the generic parameter. – 10001110101 Dec 10 '13 at 20:57
  • someDelegate(); – 10001110101 Dec 10 '13 at 20:58
  • Is it possible to do it without creating a delegate each time? I'm looking for a deeper understanding of why Delegates can't be called with a generic parameter. The delegate call someDelegate(); is really what I'm looking for. – 10001110101 Dec 10 '13 at 21:14
  • @10001110101 You cannot do that since the passed delegate has its generic parameter set because we defined it as `someDelegate = GenericMethod;` You cannot call it with a different parameter afterwards. See usr's answer for more on this. – helb Dec 10 '13 at 21:16