80

是否可以在 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;
}

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

想法?

4

8 回答 8

66

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

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

static GenericCollection()
{
    if (!typeof(T).IsSubclassOf(typeof(Delegate)))
    {
        throw new InvalidOperationException(typeof(T).Name + " is not a delegate type");
    }
}
于 2008-10-10T15:44:28.827 回答
19

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

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 中有哪些新功能?

于 2018-05-11T11:28:15.260 回答
13

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

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'

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

于 2008-10-10T15:52:24.210 回答
10

如果你愿意在编译时依赖于 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
    {
    }
}
于 2012-06-01T07:46:02.087 回答
3

委托已经支持链接。这不符合您的需求吗?

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
    }

}
于 2008-10-10T18:20:27.827 回答
3

我遇到了一种情况,我需要在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));
}

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

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)

于 2010-01-18T18:30:38.463 回答
2

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

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

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

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

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

于 2014-05-19T02:22:29.813 回答
1

根据MSDN

编译器错误 CS0702

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

  • 系统对象
  • 系统数组
  • 系统委托
  • 系统枚举
  • 系统值类型。
于 2015-04-30T08:21:17.573 回答