C# 泛型不允许委托类型约束
-
08-07-2019 - |
题
是否可以在 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;
}
但随后我就失去了为不同类型的函数重用相同定义的能力。
想法?
解决方案
许多课程不可用作通用约束 - Enum是另一个。
对于代理人来说,你能得到的最接近的是<!> quot;:class <!> quot;,也许用反射检查(例如,在静态构造函数中)T 是代表:
static GenericCollection()
{
if (!typeof(T).IsSubclassOf(typeof(Delegate)))
{
throw new InvalidOperationException(typeof(T).Name + " is not a delegate type");
}
}
其他提示
编辑: 这些文章中提出了一些建议的解决方法:
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,约束):
类类型约束必须满足以下规则:
- 该类型必须是类类型。
- 该类型不得密封。
- 该类型不得是以下类型之一:System.Array、System.Delegate、System.Enum 或 System.ValueType.
- 类型不能是对象。因为所有类型都派生自对象,所以如果允许的话,这样的约束将不会产生任何影响。
- 给定类型参数的最多一个约束可以是类类型。
果然 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
{
}
}
是的,在C#7.3中,Constraints系列可以包括Enum
,Delegate
和unmanaged
类型。
您可以毫无问题地编写此代码:
void M<D, E, T>(D d, E e, T* t) where D : Delegate where E : Enum where T : unmanaged
{
}
有用的链接:
代表已经支持链接。这不符合您的需求吗?
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
,但我想要一个通用约束。具体来说,我想使用反射添加一个事件处理程序,但我想为委托使用泛型参数。下面的代码不起作用,因为<!> quot; Handler <!> quot;是一个类型变量,编译器不会将Handler
转换为convert
:
public void AddHandler<Handler>(Control c, string eventName, Handler d) {
c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d);
}
但是,您可以传递一个为您进行转换的功能。 KeyPress
接受SomeControl_KeyPress
参数并返回<=>:
public void AddHandler<Handler>(Control c, string eventName,
Func<Delegate, Handler> convert, Handler d) {
c.GetType().GetEvent(eventName).AddEventHandler(c, convert(d));
}
现在编译器很高兴。调用方法很简单。例如,附加到Windows窗体控件上的<=>事件:
AddHandler<KeyEventHandler>(someControl,
"KeyPress",
(h) => (KeyEventHandler) h,
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
也不能用作通用约束。
如果您在IL中构建适当的调用,则可以解决此问题。它会正常工作。
这是Jon Skeet的一个很好的例子。
http://code.google.com/p/unconstrained-melody/一>
我已经从Jon Skeet的书 C#in Depth ,第3版中获取了我的参考资料。