C#泛型不允许委托类型约束
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/191940/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C# Generics won't allow Delegate Type Constraints
提问by Nicholas Mancuso
Is it possible to define a class in C# such that
是否可以在 C# 中定义一个类,以便
class GenericCollection<T> : SomeBaseCollection<T> where T : Delegate
I couldn't for the life of me accomplish this last night in .NET 3.5. I tried using
昨晚我无法在 .NET 3.5 中完成这件事。我尝试使用
delegate, Delegate, Action<T> and Func<T, T>
delegate, Delegate, Action<T> and Func<T, T>
It seems to me that this should be allowable in some way. I'm trying to implement my own EventQueue.
在我看来,这在某种程度上应该是允许的。我正在尝试实现我自己的 EventQueue。
I ended up just doing this [primitive approximation mind you].
我最终只是这样做了[请注意原始近似]。
internal delegate void DWork();
class EventQueue {
private Queue<DWork> eventq;
}
But then I lose the ability to reuse the same definition for different types of functions.
但随后我失去了为不同类型的函数重用相同定义的能力。
Thoughts?
想法?
采纳答案by Marc Gravell
A number of classes are unavailable as generic contraints - Enum being another.
许多类不可用作为通用约束 - Enum 是另一个。
For delegates, the closest you can get is ": class", perhaps using reflection to check (for example, in the static constructor) that the T isa delegate:
对于委托,您可以获得的最接近的是“:class”,也许使用反射来检查(例如,在静态构造函数中)T是委托:
static GenericCollection()
{
if (!typeof(T).IsSubclassOf(typeof(Delegate)))
{
throw new InvalidOperationException(typeof(T).Name + " is not a delegate type");
}
}
回答by Jorge Ferreira
Edit:Some proposed work-arounds are proposed in these articles:
编辑:这些文章中提出了一些建议的解决方法:
http://jacobcarpenters.blogspot.com/2006/06/c-30-and-delegate-conversion.html
http://jacobcarpenters.blogspot.com/2006/06/c-30-and-delegate-conversion.html
http://jacobcarpenters.blogspot.com/2006_11_01_archive.html
http://jacobcarpenters.blogspot.com/2006_11_01_archive.html
From the C# 2.0 specificationwe can read (20.7, Constraints):
从C# 2.0 规范我们可以读到 (20.7, Constraints):
A class-type constraint must satisfy the following rules:
类类型约束必须满足以下规则:
- The type must be a class type.
- The type must not be sealed.
- The type must not be one of the following types: System.Array, System.Delegate, System.Enum, or System.ValueType.
- The type must not be object. Because all types derive from object, such a constraint would have no effect if it were permitted.
- At most one constraint for a given type parameter can be a class type.
- 类型必须是类类型。
- 类型不得密封。
- 类型不能是以下类型之一: System.Array、System.Delegate、System.Enum 或 System.ValueType。
- 类型不能是对象。因为所有类型都从对象派生,所以如果允许的话,这样的约束将不起作用。
- 给定类型参数的最多一个约束可以是类类型。
And sure enough VS2008 spits out an error:
果然VS2008吐出一个错误:
error CS0702: Constraint cannot be special class 'System.Delegate'
For info and investigation on this issue read here.
有关此问题的信息和调查,请阅读此处。
回答by Amy B
Delegate already supports chaining. Doesn't this meet your needs?
委托已经支持链接。这不满足您的需求吗?
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
}
}
回答by Justin Bailey
I came across a situation where I needed to deal with a Delegate
internally but I wanted a generic constraint. Specifically, I wanted to add an event handler using reflection, but I wanted to use a generic argument for the delegate. The code below does NOT work, since "Handler" is a type variable, and the compiler won't cast Handler
to Delegate
:
我遇到了一种情况,我需要在Delegate
内部处理一个,但我想要一个通用的约束。具体来说,我想使用反射添加一个事件处理程序,但我想为委托使用一个通用参数。下面的代码不起作用,因为“处理程序”是一个类型变量,编译器不会强制转换Handler
为Delegate
:
public void AddHandler<Handler>(Control c, string eventName, Handler d) {
c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d);
}
However, you can pass a function that does the conversion for you. convert
takes a Handler
argument and returns a Delegate
:
但是,您可以传递一个为您进行转换的函数。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));
}
Now the compiler is happy. Calling the method is easy. For example, attaching to the KeyPress
event on a Windows Forms control:
现在编译器很高兴。调用方法很容易。例如,附加到KeyPress
Windows 窗体控件上的事件:
AddHandler<KeyEventHandler>(someControl,
"KeyPress",
(h) => (KeyEventHandler) h,
SomeControl_KeyPress);
where SomeControl_KeyPress
is the event target. The key is the converter lambda - it does no work, but it convinces the compiler you gave it a valid delegate.
SomeControl_KeyPress
事件目标在哪里。关键是转换器 lambda - 它不起作用,但它说服编译器你给了它一个有效的委托。
(Begin 280Z28) @Justin: Why not use this?
(开始 280Z28)@Justin:为什么不使用这个?
public void AddHandler<Handler>(Control c, string eventName, Handler d) {
c.GetType().GetEvent(eventName).AddEventHandler(c, d as Delegate);
}
(End 280Z28)
(完结280Z28)
回答by Simon
If you are willing to take a compile time dependency on an IL Weaver you can do this with Fody.
如果您愿意对 IL Weaver 进行编译时依赖,您可以使用Fody做到这一点。
Using this addin to Fody https://github.com/Fody/ExtraConstraints
将此插件用于 Fody https://github.com/Fody/ExtraConstraints
Your code can look like this
您的代码可能如下所示
public class Sample
{
public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
{
}
public void MethodWithEnumConstraint<[EnumConstraint] T>()
{
}
}
And be compiled to this
并编译到这个
public class Sample
{
public void MethodWithDelegateConstraint<T>() where T: Delegate
{
}
public void MethodWithEnumConstraint<T>() where T: struct, Enum
{
}
}
回答by maxspan
As mentioned above, you cannot have Delegates and Enum as a generic constraint. System.Object
and System.ValueType
also cannot be used as a generic constraint.
如上所述,您不能将 Delegates 和 Enum 作为通用约束。System.Object
并且System.ValueType
也不能用作通用约束。
The work around can be if you construct an appropriate call in you IL. It will work fine.
如果您在 IL 中构建适当的调用,则可以解决此问题。它会正常工作。
Here is a good example by Jon Skeet.
这是 Jon Skeet 的一个很好的例子。
http://code.google.com/p/unconstrained-melody/
http://code.google.com/p/unconstrained-melody/
I have taken my references from Jon Skeet's book C# in Depth, 3rd edition.
我参考了 Jon Skeet 的书C# in Depth,第 3 版。
回答by Rahul Nikate
回答by mshwf
Yes it's possible in C# 7.3, Constraints family increased to include Enum
, Delegate
and unmanaged
types.
You can write this code without a problem:
是的,在 C# 7.3 中是可能的,约束系列增加到包括Enum
,Delegate
和unmanaged
类型。您可以毫无问题地编写此代码:
void M<D, E, T>(D d, E e, T* t) where D : Delegate where E : Enum where T : unmanaged
{
}
Useful links:
有用的链接:
The future of C#, from Microsoft Build 2018
C# 的未来,来自 Microsoft Build 2018