C#generics将不允许委托types约束

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

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

在.NET 3.5中我无法完成这一切。 我试过使用

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

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

我最终只是做了这个[原始逼近你]。

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

但是后来我失去了对不同types的函数重复使用相同定义的能力。

思考?

一些类不能作为通用的限制 – 枚举是另一个。

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

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

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

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

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


从C#2.0规范中我们可以读到(20.7,Constraints):

types约束必须满足以下规则:

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

当然,VS2008吐出一个错误:

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

有关此问题的信息和调查请阅读此处 。

如果你愿意在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 { } } 

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

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

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

 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)

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

如果你在你身上build立一个适当的电话,解决方法可以是IL。 它会正常工作。

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

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

我已经从Jon Skeet的C#书第三版中获得了我的参考资料。

根据MSDN

编译器错误CS0702

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

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