代码之家  ›  专栏  ›  技术社区  ›  Nicholas Mancuso

C#泛型不允许委托类型约束

  •  85
  • Nicholas Mancuso  · 技术社区  · 17 年前

    是否有可能在C#中定义一个类,以便

    class GenericCollection<T> : SomeBaseCollection<T> where T : Delegate
    

    昨晚我无论如何也做不到这一点。净3.5。我试着用

    delegate, Delegate, Action<T> and Func<T, T>

    在我看来,这在某种程度上应该是允许的。我正在尝试实现自己的EventQueue。

    我最终只是做了这个[请注意,原始近似]。

    internal delegate void DWork();
    
    class EventQueue {
        private Queue<DWork> eventq;
    }
    

    但是,我失去了为不同类型的函数重用相同定义的能力。

    思想?

    8 回复  |  直到 13 年前
        1
  •  66
  •   Eduard Malakhov    8 年前

    许多类不可用作泛型约束——枚举是另一个。

    对于委托,你能得到的最接近的结果是“:class”,也许可以使用反射来检查(例如,在静态构造函数中)T 代表:

    static GenericCollection()
    {
        if (!typeof(T).IsSubclassOf(typeof(Delegate)))
        {
            throw new InvalidOperationException(typeof(T).Name + " is not a delegate type");
        }
    }
    
        2
  •  19
  •   mshwf    5 年前

    是的,在C#7.3中,约束家族增加到包括 Enum , Delegate unmanaged 类型。 你可以毫无问题地编写这段代码:

    void M<D, E, T>(D d, E e, T* t) where D : Delegate where E : Enum where T : unmanaged
        {
    
        }
    

    From Docs :

    从C#7.3开始,您可以使用非托管约束来指定 类型参数必须是不可为null的非托管类型。这个 非托管约束使您能够编写可重用的例程来工作 类型可以作为内存块进行操作

    有用链接:

    The future of C# ,来自微软Build 2018

    What's new in C# 7.3?

        3
  •  13
  •   Jorge Ferreira    17 年前

    编辑: 这些文章中提出了一些建议的解决方案:

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

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


    C# 2.0 specification 我们可以读到(20.7,约束):

    类类型约束必须满足以下规则:

    • 类型必须是类类型。
    • 该类型不得密封。
    • 类型不能是以下类型之一:系统。阵列、系统。系统代表。枚举或系统。值类型 .
    • 类型不能是对象。因为所有类型都派生自对象,所以如果允许,这样的约束将无效。
    • 对于给定的类型参数,最多只能有一个约束是类类型。

    果然,VS2008抛出了一个错误:

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

    有关此问题的信息和调查,请阅读 here .

        4
  •  10
  •   Şafak Gür    12 年前

    如果你愿意在编译时依赖IL Weaver,你可以用 Fody .

    将此插件用于Fody https://github.com/Fody/ExtraConstraints

    你的代码可以看起来像这样

    public class Sample
    {
        public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
        {        
        }
        public void MethodWithEnumConstraint<[EnumConstraint] T>()
        {
        }
    } 
    

    并被编译成这个

    public class Sample
    {
        public void MethodWithDelegateConstraint<T>() where T: Delegate
        {
        }
    
        public void MethodWithEnumConstraint<T>() where T: struct, Enum
        {
        }
    }
    
        5
  •  3
  •   Amy B    17 年前

    我遇到了一个需要处理的情况 Delegate 在内部,但我想要一个通用的约束。具体来说,我想使用反射添加一个事件处理程序,但我想为委托使用一个通用参数。下面的代码不起作用,因为“Handler”是一个类型变量,编译器不会强制转换 Handler 代表 :

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

    但是,您可以传递一个为您进行转换的函数。 convert 需要a 经办人 argument并返回a 代表 :

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

    现在编译器很高兴。调用该方法很容易。例如,附加到 KeyPress Windows窗体控件上的事件:

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

    哪里 SomeControl_KeyPress 是事件目标。关键是转换器lambda——它不起作用,但它让编译器相信你给了它一个有效的委托。

    (从280Z28开始)@Justin:为什么不使用这个?

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

    (280Z28结束)

        6
  •  3
  •   Sam Harwell    16 年前

    Delegate已经支持链接。这不符合你的需求吗?

    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
        }
    
    }
    
        7
  •  2
  •   Stephen Kennedy annamataws    7 年前

    如上所述,您不能将委托和枚举作为泛型约束。 System.Object System.ValueType 也不能用作通用约束。

    如果你在你的IL中构建一个适当的调用,那么解决方法就是。它会很好地工作。

    这是Jon Skeet的一个很好的例子。

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

    我引用了乔恩·斯基特的书 C#深度 ,第三版。

        8
  •  1
  •   Rahul Nikate    11 年前

    根据 MSDN

    编译器错误CS0702

    约束不能是特殊类“标识符”。以下类型不能用作约束:

    • 系统。对象
    • 系统。阵列
    • 系统。代表
    • 系统。枚举
    • 系统。值类型。