具有自引用类型约束的generics类

请考虑以下代码:

abstract class Foo where T : Foo, new() { void Test() { if(Bar != null) Bar(this); } public event Bar Bar; } delegate void Bar(T foo) where T : Foo, new(); 

Bar(this)导致以下编译器错误:
参数类型为Foo 的参数类型不可分配

T被约束为Foo ,因为我希望派生类基本上告诉基类它们的类型,以便可以在事件回调中使用该类型,以便保存实现者不必将回调参数强制转换为派生类型。

我可以看到代码不能正常工作,但我对如何正确执行此操作有一点障碍,而不会使用可用于任何旧事物的generics委托。 我也不太确定为什么T约束不会产生编译器错误,因为它似乎是递归的。

编辑

我想要澄清这一点! 这是一个新的例子,我希望会更清楚。 请注意,下面的OnDuckReady事件处理程序会生成编译器错误。

如何让事件以正确的类型传递?

 abstract class Animal where T : Animal, new() { void Test() { if(AnimalReady != null) AnimalReady(this); } public event AnimalHandler AnimalReady; } delegate void AnimalHandler(Animal animal) where T : Animal, new(); class Duck : Animal { public void FlyAway() { } } class Test { void Main() { Duck duck = new Duck(); duck.AnimalReady += OnDuckReady; // COMPILER ERROR } void OnDuckReady(Duck duck) { duck.FlyAway(); } } 

你可以将’this’转换为T:

 Bar((T)this); 

但是,如果您有以下内容,则会失败:

 public class MyFoo : Foo { } public class MyOtherFoo : Foo { } 

因为’MyOtherFoo’不是’MyFoo’的实例。 看看C#设计师之一Eric Lippert的这篇文章。

 delegate void Bar(Foo foo) where T : Foo, new(); 

它很棒。 我测试了它。

这是测试代码

 public abstract class Foo where T :Foo { public event Bar Bar; public void Test () { if (Bar != null) { Bar (this); } } } public class FooWorld : Foo { } public delegate void Bar(Foo foo) where T : Foo; class MainClass { public static void Main (string[] args) { FooWorld fw = new FooWorld (); fw.Bar += delegate(Foo foo) { Console.WriteLine ("Bar response to {0}", foo); }; fw.Test (); } } 

如果您没有将“Bar”用于两个目的,代码会更清晰。 话虽如此,我认为需要的是使用带有两个参数的通用(例如T和U),使得T从U派生,而U派生自Foo。 或者,可以使用接口做一些不错的事情。 一个有用的模式是定义:

 interface ISelf {T Self {get;}} 

然后,对于人们可能想要在对象中组合的各种接口:

 interface IThis : IThis, ISelf {} interface IThat : IThat, ISelf {} interface ITheOtherThing : ITheOtherThing, ISelf {} 

如果实现IThis,IThat和ITheOtherThing的类也实现了ISelf < theirOwnTypes >,那么可以有一个例程,其参数(例如“foo”)必须实现IThis和IThat接受参数类型为IThis。 参数“foo”将是IThis类型(反过来实现IThis),而Foo.Self将是IThat类型。 请注意,如果以这种方式实现,可以自由地将变量类型转换为任何所需的接口组合。 例如,在上面的示例中,如果作为“foo”传递的对象是实现IThis,IThat,ITheOtherThing和ISelf < itsOwnType >的类型,则可以对ITheOtherThing>或IThis,或任何其他所需的组合和排列进行类型转换。那些接口。

真的是一个多才多艺的技巧。

编辑/编

这是一个更完整的例子。

 namespace ISelfTester { interface ISelf {T Self {get;} } interface IThis { void doThis(); } interface IThat { void doThat(); } interface IOther { void doOther(); } interface IThis : IThis, ISelf {} interface IThat : IThat, ISelf {} interface IOther : IOther, ISelf {} class ThisOrThat : IThis, IThat { public ThisOrThat Self { get { return this; } } public void doThis() { Console.WriteLine("{0}.doThis", this.GetType()); } public void doThat() { Console.WriteLine("{0}.doThat", this.GetType()); } } class ThisOrOther : IThis, IOther { public ThisOrOther Self { get { return this; } } public void doThis() { Console.WriteLine("{0}.doThis", this.GetType()); } public void doOther() { Console.WriteLine("{0}.doOther", this.GetType()); } } class ThatOrOther : IThat, IOther { public ThatOrOther Self { get { return this; } } public void doThat() { Console.WriteLine("{0}.doThat", this.GetType()); } public void doOther() { Console.WriteLine("{0}.doOther", this.GetType()); } } class ThisThatOrOther : IThis,IThat, IOther { public ThisThatOrOther Self { get { return this; } } public void doThis() { Console.WriteLine("{0}.doThis", this.GetType()); } public void doThat() { Console.WriteLine("{0}.doThat", this.GetType()); } public void doOther() { Console.WriteLine("{0}.doOther", this.GetType()); } } static class ISelfTest { static void TestThisOrThat(IThis param) { param.doThis(); param.Self.doThat(); } static void TestThisOrOther(IThis param) { param.doThis(); param.Self.doOther(); } static void TestThatOrOther(IThat param) { param.doThat(); param.Self.doOther(); } public static void test() { IThis ThisOrThat1 = new ThisOrThat(); IThat ThisOrThat2 = new ThisOrThat(); IThis ThisOrOther1 = new ThisOrOther(); IOther OtherOrThat1 = new ThatOrOther(); IThis> ThisThatOrOther1 = new ThisThatOrOther(); IOther> ThisThatOrOther2a = new ThisThatOrOther(); var ThisThatOrOther2b = (IOther>)ThisThatOrOther1; TestThisOrThat(ThisOrThat1); TestThisOrThat((IThis)ThisOrThat2); TestThisOrThat((IThis)ThisThatOrOther1); TestThisOrOther(ThisOrOther1); TestThisOrOther((IThis)ThisThatOrOther1); TestThatOrOther((IThat)OtherOrThat1); TestThatOrOther((IThat)ThisThatOrOther1); } } } 

需要注意的是,有些类实现了IThis,IThat和IOther的不同组合,有些方法需要不同的组合。 上面给出的四个非静态类都是不相关的,接口IThisIThatIOther 。 尽管如此,如果实现类遵循指示的模式,则方法参数可能需要接口的任何组合。 “组合”接口类型的存储位置只能传递给以相同顺序指定所包含接口的参数。 然而,任何正确实现模式的类型的实例可以使用任何顺序的任何子接口(具有或不具有重复)来对任何“组合”接口类型进行类型转换。 当与正确实现模式的类的实例一起使用时,类型转换将始终在运行时成功(它们可能会因恶意实现而失败)。