C# Generics won't allow Delegate Type Constraints

前端 未结 8 1235
别跟我提以往
别跟我提以往 2020-11-28 08:01

Is it possible to define a class in C# such that

class GenericCollection : SomeBaseCollection where T : Delegate

I couldn

相关标签:
8条回答
  • 2020-11-28 08:10

    Edit: Some proposed work-arounds are proposed in these articles:

    http://jacobcarpenters.blogspot.com/2006/06/c-30-and-delegate-conversion.html

    http://jacobcarpenters.blogspot.com/2006_11_01_archive.html


    From the C# 2.0 specification we can read (20.7, Constraints):

    A class-type constraint must satisfy the following rules:

    • The type must be a class type.
    • The type must not be sealed.
    • The type must not be one of the following types: System.Array, System.Delegate, System.Enum, or System.ValueType.
    • The type must not be object. Because all types derive from object, such a constraint would have no effect if it were permitted.
    • At most one constraint for a given type parameter can be a class type.

    And sure enough VS2008 spits out an error:

    error CS0702: Constraint cannot be special class 'System.Delegate'
    

    For info and investigation on this issue read here.

    0 讨论(0)
  • 2020-11-28 08:19

    A number of classes are unavailable as generic contraints - Enum being another.

    For delegates, the closest you can get is ": class", perhaps using reflection to check (for example, in the static constructor) that the T is a delegate:

    static GenericCollection()
    {
        if (!typeof(T).IsSubclassOf(typeof(Delegate)))
        {
            throw new InvalidOperationException(typeof(T).Name + " is not a delegate type");
        }
    }
    
    0 讨论(0)
  • 2020-11-28 08:22

    As mentioned above, you cannot have Delegates and Enum as a generic constraint. System.Object and System.ValueType also cannot be used as a generic constraint.

    The work around can be if you construct an appropriate call in you IL. It will work fine.

    Here is a good example by Jon Skeet.

    http://code.google.com/p/unconstrained-melody/

    I have taken my references from Jon Skeet's book C# in Depth, 3rd edition.

    0 讨论(0)
  • 2020-11-28 08:27

    Delegate already supports chaining. Doesn't this meet your needs?

    public class EventQueueTests
    {
        public void Test1()
        {
            Action myAction = () => Console.WriteLine("foo");
            myAction += () => Console.WriteLine("bar");
    
            myAction();
            //foo
            //bar
        }
    
        public void Test2()
        {
            Action<int> myAction = x => Console.WriteLine("foo {0}", x);
            myAction += x => Console.WriteLine("bar {0}", x);
            myAction(3);
            //foo 3
            //bar 3
        }
    
        public void Test3()
        {
            Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; };
            myFunc += x => { Console.WriteLine("bar {0}", x); return x + 1; };
            int y = myFunc(3);
            Console.WriteLine(y);
    
            //foo 3
            //bar 3
            //4
        }
    
        public void Test4()
        {
            Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; };
            Func<int, int> myNextFunc = x => { x = myFunc(x);  Console.WriteLine("bar {0}", x); return x + 1; };
            int y = myNextFunc(3);
            Console.WriteLine(y);
    
            //foo 3
            //bar 5
            //6
        }
    
    }
    
    0 讨论(0)
  • 2020-11-28 08:32

    I came across a situation where I needed to deal with a Delegate internally but I wanted a generic constraint. Specifically, I wanted to add an event handler using reflection, but I wanted to use a generic argument for the delegate. The code below does NOT work, since "Handler" is a type variable, and the compiler won't cast Handler to Delegate:

    public void AddHandler<Handler>(Control c, string eventName, Handler d) {
      c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d);
    }
    

    However, you can pass a function that does the conversion for you. convert takes a Handler argument and returns a Delegate:

    public void AddHandler<Handler>(Control c, string eventName, 
                      Func<Delegate, Handler> convert, Handler d) {
          c.GetType().GetEvent(eventName).AddEventHandler(c, convert(d));
    }
    

    Now the compiler is happy. Calling the method is easy. For example, attaching to the KeyPress event on a Windows Forms control:

    AddHandler<KeyEventHandler>(someControl, 
               "KeyPress", 
               (h) => (KeyEventHandler) h,
               SomeControl_KeyPress);
    

    where SomeControl_KeyPress is the event target. The key is the converter lambda - it does no work, but it convinces the compiler you gave it a valid delegate.

    (Begin 280Z28) @Justin: Why not use this?

    public void AddHandler<Handler>(Control c, string eventName, Handler d) { 
      c.GetType().GetEvent(eventName).AddEventHandler(c, d as Delegate); 
    } 
    

    (End 280Z28)

    0 讨论(0)
  • 2020-11-28 08:34

    According to MSDN

    Compiler Error CS0702

    Constraint cannot be special class 'identifier' The following types may not be used as constraints:

    • System.Object
    • System.Array
    • System.Delegate
    • System.Enum
    • System.ValueType.
    0 讨论(0)
提交回复
热议问题