繁体   English   中英

具有自引用类型约束的通用 class

[英]Generic class with self-referencing type constraint

考虑以下代码:

abstract class Foo<T>
    where T : Foo<T>, new()
{
    void Test()
    {
        if(Bar != null)
            Bar(this);
    }

    public event Bar<T> Bar;
}

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

Bar(this)导致以下编译器错误:
参数类型 Foo<T> 不可分配给参数类型 T

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

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

编辑

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

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

abstract class Animal<T>
    where T : Animal<T>, new()
{
    void Test()
    {
        if(AnimalReady != null)
            AnimalReady(this);
    }

    public event AnimalHandler<T> AnimalReady;
}

delegate void AnimalHandler<T>(Animal<T> animal)
    where T : Animal<T>, new();

class Duck : Animal<Duck>
{
    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<MyFoo> { }

public class MyOtherFoo : Foo<MyFoo> { }

因为“MyOtherFoo”不是“MyFoo”的一个实例。 看看 Eric Lippert 的这篇文章,他是 C# 的设计者之一。

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

它工作得很好。 我测试了它。

这是测试代码

public abstract class Foo<T> where T :Foo<T> {
    public event Bar<T> Bar;

    public void Test ()
    {
        if (Bar != null)
        {
            Bar (this);
        }
    }
}

public class FooWorld : Foo<FooWorld> {
}

public delegate void Bar<T>(Foo<T> foo) where T : Foo<T>;

class MainClass
{
    public static void Main (string[] args)
    {
        FooWorld fw = new FooWorld ();
        fw.Bar += delegate(Foo<FooWorld> foo) {
            Console.WriteLine ("Bar response to {0}", foo);
        };

        fw.Test ();
    }
}

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

interface ISelf<out T> {T Self<T> {get;}}

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

interface IThis<out T> : IThis, ISelf<T> {}
interface IThat<out T> : IThat, ISelf<T> {}
interface ITheOtherThing<out T> : ITheOtherThing, ISelf<T> {}

>, one can then have a routine whose parameter (eg "foo") has to implement both IThis and IThat accept the parameter as type IThis.如果实现 IThis、IThat 和 ITheOtherThing 的类也实现了 ISelf< >,则可以有一个例程,其参数(例如“foo”)必须同时实现 IThis 和 IThat,并将参数作为类型 IThis 接受。 参数“foo”将是 IThis 类型(进而实现 IThis),而 Foo.Self 将是 IThat 类型。 请注意,如果以这种方式实现,则可以自由地将变量类型转换为任何所需的接口组合。 > it could be typecast to ITheOtherThing>, or IThis, or any other desired combination and arrangement of those interfaces.例如,在上面的示例中,如果作为“foo”传递的 object 是实现 IThis、IThat、ITheOtherThing 和 ISelf< > 的类型,则它可以被类型转换为 ITheOtherThing> 或 IThis 或任何其他所需的组合和排列那些接口。

真的是一个非常多才多艺的把戏。

编辑/附录

这是一个更完整的例子。

namespace ISelfTester
{
    interface ISelf<out T> {T Self {get;} }

    interface IThis { void doThis(); }
    interface IThat { void doThat(); }
    interface IOther { void doOther(); }

    interface IThis<out T> : IThis, ISelf<T> {}
    interface IThat<out T> : IThat, ISelf<T> {}
    interface IOther<out T> : IOther, ISelf<T> {}

    class ThisOrThat : IThis<ThisOrThat>, IThat<ThisOrThat>
    {
        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<ThisOrOther>, IOther<ThisOrOther>
    {
        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<ThatOrOther>, IOther<ThatOrOther>
    {
        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<ThisThatOrOther>,IThat<ThisThatOrOther>, IOther<ThisThatOrOther>
    {
        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<IThat> param)
        {
            param.doThis();
            param.Self.doThat();
        }
        static void TestThisOrOther(IThis<IOther> param)
        {
            param.doThis();
            param.Self.doOther();
        }
        static void TestThatOrOther(IThat<IOther> param)
        {
            param.doThat();
            param.Self.doOther();
        }

        public static void test()
        {
            IThis<IThat> ThisOrThat1 = new ThisOrThat();
            IThat<IThis> ThisOrThat2 = new ThisOrThat();
            IThis<IOther> ThisOrOther1 = new ThisOrOther();
            IOther<IThat> OtherOrThat1 = new ThatOrOther();
            IThis<IThat<IOther>> ThisThatOrOther1 = new ThisThatOrOther();
            IOther<IThat<IThis>> ThisThatOrOther2a = new ThisThatOrOther();
            var ThisThatOrOther2b = (IOther<IThis<IThat>>)ThisThatOrOther1;
            TestThisOrThat(ThisOrThat1);
            TestThisOrThat((IThis<IThat>)ThisOrThat2);
            TestThisOrThat((IThis<IThat>)ThisThatOrOther1);
            TestThisOrOther(ThisOrOther1);
            TestThisOrOther((IThis<IOther>)ThisThatOrOther1);
            TestThatOrOther((IThat<IOther>)OtherOrThat1);
            TestThatOrOther((IThat<IOther>)ThisThatOrOther1);
        }
    }
}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM