簡體   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