簡體   English   中英

動態鑄造C#

[英]Dynamic Casting C#

我有家長班X和家長班H。

H在其字段數據中有對類型X的引用。H的構造函數需要類型X的實例,然后將其存儲在引用中。

然后,我從X派生一個子類x1,從H派生一個子類h1。

h1將在其構造函數中接受X或x1的實例。

但是這是不是已經在它的父類X定義X1的方法和屬性將不會提供給H1。

如何在班級h1中將x1永久轉換為x1的類型?

讓我們首先將問題重寫為可理解的東西。 我們將其稱為X和H,而不是X和H。 您的情況是:

abstract class Animal
{
    protected Food favourite;
    protected Animal(Food f) { this.favourite = f; }
}
abstract class Food
{
}
sealed class Banana : Food 
{
    public void Peel() {}
}
sealed class Monkey : Animal
{
    public Monkey(Banana banana) : base(banana) {}
    public PeelMyBanana()
    {
        this.favourite.Peel(); // Error, favourite is of type Food, not Banana
    }
}

您的問題是:

如何在“猴子”中永久性地使“收藏夾”類型為“香蕉”?

簡短的答案是:您不能。 該字段是給定類型的,可以包含對Food類型實例的任何引用。 編譯器和運行時並不知道或擔心您碰巧知道實際上將哪些類型的事物分配給該字段的更嚴格的限制。

有多種方法可以解決此問題。 您可以添加一個為您執行轉換的訪問器:

sealed class Monkey : Animal
{
    public Monkey(Banana banana) : base(banana) {}

    private Banana Favourite { get { return (Banana)this.favourite; } }        

    public PeelMyBanana()
    {
        this.Favourite.Peel(); // Works
    }
}

或者您可以泛化基類:

abstract class Animal<F> where F : Food
{
    protected F favourite;
    protected Animal(F f) { this.favourite = f; }
}
abstract class Food
{
}
sealed class Banana : Food 
{
    public void Peel() {}
}
sealed class Monkey : Animal<Banana>
{
    public Monkey(Banana banana) : base(banana) {}
    public PeelMyBanana()
    {
        this.favourite.Peel(); // Legal; Animal<Banana>.favourite is of type Banana
    }
}

沒有“永久”強制轉換(或者更確切地說,“永久強制”轉換的想法是荒謬的),因為這意味着強制轉換對對象執行了某些操作 ,但實際上並沒有,因此沒有任何可持久性或撤消)。 您的變量的類型為H ,並且變量的類型永遠不會改變。 您的實例的類型為H1 ,實例的類型永遠不會改變。 強制轉換只是告訴編譯器“是的,即使我僅將該實例引用為H類型,但實際上我知道它的類型為H1 ,因此將其存儲在可以引用H1實例的任何變量中都是安全的。”

如果您的結構如下所示:

class H
{

}

class X
{
    public H HValue { get; private set; }

    public X(H h)
    {
        HValue = h;
    }
}

class H1 : H
{
    public void Foo() { }
}

class X1 : X
{
    public X1(H1 h1) : base(h1)
    {

    }
}

然后,如果您始終想使用該值而不進行向下轉換,則必須將h1變量的值存儲在其他位置。

現在,以某種“臭味”的方式完成您想要的事情(不必每次都編寫強制轉換代碼),您可以這樣做:

class X1 : X
{
    public X1(H1 h) : base(h) { }

    public new H1 HValue 
    { 
        get { return (H1)base.HValue; }
    }
}

這將允許您參考命名相同的屬性HValue任何地方,你有一個參考X1在類型的變量X1 所以...

X x = new X1(new H1());  // x.HValue would be of type H, even though the 
                         // reference itself is H1
X1 x = new X1(new H1()); // x.HValue would be of type H1

如果我理解正確,那么您可以參加以下課程。

class X
{
}

class X1 : X
{
}

class H
{
    H(X x)
    {
        MyX = x;
    }

    X MyX { get; private set; }
}

class H1 : H
{
    H1(X x) : base(x)
    {
    }
}

您希望H1處理MyX,就像它是更特定的X1類型一樣。

最簡單的方法是在H1上擁有另一個將x引用為X1的屬性:

class H1 : H
{
    H1(X1 x) : base(x)
    {
        MyX1 = x;
    }

    X1 MyX1 { get; private set; }
}

通過在H1上創建僅接受X1(而不接受任何舊X)的構造函數,可以確保始終正確設置它。

這就是泛型的用途。 如果我正確理解了您的要求,則您的H類應該在某些C上通用(H <C>類),其中C是從X派生的,以便可以將H編寫為可與X一起使用,但是H1可以是定義為源自H <X1>,以便它可以與X1一起使用。

編輯:當然,根據這些類的復雜程度,可能不值得使H泛型。 在這種情況下,我會同意馬特·迪拉德的回答。

我認為你不能。

您可以向h1添加類型為x1的字段,並在h1的構造函數中給定x1的情況下進行設置,然后使用該字段訪問要訪問的h1中的x1的方法(請記住,可能未設置x1字段)。

從您的描述中還不清楚h1構造函數是否類似於:

h1(X xInstance)

要么

h1(X xinstance, x1 x1Instance)

但我假設第一個,即使您說“ h1將在其構造函數中接受X和x1的實例”。 否則您會知道您有一個x1,因此它變得相當簡單:)

所以我想你要這樣做:

public class H1: H
{
    private X m_x;
    private X1 m_x1;

    public H1(X x):base(x)
    {
        m_x=x;
        X1 x1 = x as X1;
        if (x1!=null)
            m_x1=x1;
    }
}

您可以使用Interface,所以H沒有對X的引用,而對IX的引用,則Xx1實現IX方法/屬性。

如果您以.Net 4.0為目標,則可以使用此處描述的新動態類型。 它在功能上允許您做您想做的事情,但是從設計的角度來看,它在編譯時就不支持強類型檢查的概念。

對我來說,這聽起來像是個狡猾的設計。 聽起來您遇到了“並行類繼承”問題。

沒有更多細節,我無法給出更具體的建議。 一種可能對您有幫助的設計模式稱為“橋梁”。 但是,比我聰明的人寫了很好的一般建議,因此,我將向您介紹他們的書:

Martin Fowler-重構Joshua Kerievsky-重構為模式Kent Beck-實現模式(通過重新考慮抽象來解決此問題提供了一個很好的例子)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM