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