【问题标题】:C# Generics won't allow Delegate Type ConstraintsC# 泛型不允许委托类型约束
【发布时间】:2010-09-16 13:21:42
【问题描述】:

是否可以在 C# 中定义一个类

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

昨晚我无法在 .NET 3.5 中完成此任务。我尝试使用

delegate, Delegate, Action&lt;T&gt; and Func&lt;T, T&gt;

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

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

internal delegate void DWork();

class EventQueue {
    private Queue<DWork> eventq;
}

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

想法?

【问题讨论】:

    标签: c# generics events delegates constraints


    【解决方案1】:

    许多类不能用作通用约束 - Enum 是另一个。

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

    static GenericCollection()
    {
        if (!typeof(T).IsSubclassOf(typeof(Delegate)))
        {
            throw new InvalidOperationException(typeof(T).Name + " is not a delegate type");
        }
    }
    

    【讨论】:

    • +1 用于:1) 使用静态构造函数和 2) 由于围绕类型初始化的奇怪调试条件而包含详细消息。
    • @MarcGravell:在静态初始化程序中抛出异常不会违反CA1065: Do not raise exceptions in unexpected locations ...我一直认为您应该使用自定义代码分析规则来查找类的无效用法通常在运行时不可用。
    • 从 C# 7.3(2018 年 5 月发布)开始,允许像 where T : Delegate 这样进行约束(并且有人在下面发布了一个新的答案)。
    【解决方案2】:

    是的,在 C# 7.3 中是可能的,约束系列增加到包括 EnumDelegateunmanaged 类型。 您可以毫无问题地编写此代码:

    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 开始,可以使用非托管约束来指定 类型参数必须是不可为空的非托管类型。这 非托管约束使您能够编写可重用的例程来工作 可以作为内存块操作的类型

    有用的链接:

    The future of C#,来自 Microsoft Build 2018

    What's new in C# 7.3?

    【讨论】:

    • 是的,在 C# 7.3 中是可能的(自 2018 年 5 月起),你可以看到release notes here
    • 这应该是新接受的答案,目前的答案是 2008 年的。现在已经过时了。
    【解决方案3】:

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

    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,约束):

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

    • 类型必须是类类型。
    • 该类型不得密封。
    • 类型不得为以下类型之一:System.Array、System.Delegate、System.Enum 或 System.ValueType
    • 类型不能是对象。因为所有类型都派生自对象,所以如果允许,这样的约束将不起作用。
    • 给定类型参数的最多一个约束可以是类类型。

    果然VS2008吐出一个错误:

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

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

    【讨论】:

      【解决方案4】:

      如果您愿意在编译时依赖于 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】:

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

      【讨论】:

      • 这不是我真正想要的功能...我试图对我的泛型类进行类型约束...
      【解决方案6】:

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

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

      但是,您可以传递一个为您进行转换的函数。 convert 接受 Handler 参数并返回 Delegate

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

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

      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)

      【讨论】:

      • @Justin:我编辑了你的答案,把我的评论放在最后,因为它有一个代码块。
      【解决方案7】:

      如上所述,您不能将 Delegates 和 Enum 作为通用约束。 System.ObjectSystem.ValueType 也不能用作通用约束。

      如果您在 IL 中构建适当的调用,则可以解决此问题。它会正常工作的。

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

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

      我参考了 Jon Skeet 的书C# in Depth,第 3 版。

      【讨论】:

        【解决方案8】:

        根据MSDN

        编译器错误 CS0702

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

        • System.Object
        • System.Array
        • System.Delegate
        • System.Enum
        • System.ValueType.

        【讨论】:

        • 为什么要在这里重复这个问题?你没有告诉我们任何新的东西。
        猜你喜欢
        • 1970-01-01
        • 2022-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多