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,编译器不会将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)
如上所述,您不能将Delegates和Enum作为通用约束。 System.Object
和System.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。