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

有没有可能在 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;
}

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

有什么想法吗?

30195 次浏览

许多类作为泛型约束不可用-Enum 是另一个。

对于委托,最接近的方法是“ : class”,也许可以使用反射来检查(例如,在静态构造函数中) 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.Committee、 System.Enum 或 System.ValueType
  • 类型不能是对象。因为所有类型都是从对象派生的,所以如果允许这样的约束,它就不会产生任何效果。
  • 给定类型参数的最多一个约束可以是类类型。

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

error CS0702: Constraint cannot be special class 'System.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
}


}

我遇到了一种情况,我需要在内部处理一个 Delegate,但我想要一个通用的约束。具体来说,我希望使用反射添加一个事件处理程序,但是我想为委托使用一个通用参数。下面的代码不起作用,因为“ Handler”是一个类型变量,编译器不会将 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)@贾斯汀: 为什么不用这个?

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

(完)

如果你愿意采取编译时间依赖于一个 IL 编织器,你可以做到这一点与 福迪

使用这个插件到福迪 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
{
}
}

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

如果你在你的 IL 中构造了一个合适的调用,那么就可以解决这个问题。

下面是 Jon Skeet 的一个很好的例子。

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

我的参考资料来自乔恩 · 斯基特的书 深度 C # ,第三版。

根据 MSDN

编译器错误 CS0702

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

  • 系统目标
  • 系统数组
  • 系统,授权
  • 系统。枚举
  • System.ValueType.

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

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


}

来自文件 :

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

有用连结:

C # 的未来,来自 Microsoft Build 2018

C # 7.3有什么新内容吗?