2011-07-22 89 views
28

正如您在下面的代碼中看到的,DoStuff()方法在構造一個Child對象期間在Init()之前被調用。C#如何在對象構造後執行代碼(後構造)

我現在處於一個有許多孩子班的情況。因此,在每個子的構造函數中直接在Init()之後重複調用DoStuff()方法並不是一個優雅的解決方案。

有沒有什麼辦法可以在父類中創建某種類型的構造函數,並在子構造函數之後執行?這樣,我可以在那裏調用DoStuff()方法。

如果您有任何其他的設計理念可以解決我的問題,我也想聽聽!

abstract class Parent 
{ 
    public Parent() 
    { 
     DoStuff(); 
    } 

    protected abstract void DoStuff(); 
} 

class Child : Parent 
{ 
    public Child() 
    // DoStuff is called here before Init 
    // because of the preconstruction 
    { 
     Init(); 
    } 

    private void Init() 
    { 
     // needs to be called before doing stuff 
    } 

    protected override void DoStuff() 
    { 
     // stuff 
    } 
} 
+0

找到顯示構建順序的鏈接... http://www.csharp411.com/c-object-initialization/基本上指出您的派生實例構造函數是要做的東西的最後一個。我認爲將DoStuff從基類構造函數中提取出來並明確地調用它是最直接的方法,或者提供可以被覆蓋的基類Init。 – deepee1

+0

+1:我不知道爲什麼,但我總是(錯誤地)認爲子構造函數首先被調用;這就是爲什麼我總是在我的構造函數調用中使用base()。謝謝你教育我。 – NotMe

回答

7

如何:

abstract class Parent 
{ 
    public Parent() 
    { 
     Init(); 
     DoStuff(); 
    } 

    protected abstract void DoStuff(); 
    protected abstract void Init(); 
} 

class Child : Parent 
{ 
    public Child() 
    { 
    } 

    protected override void Init() 
    { 
     // needs to be called before doing stuff 
    } 

    protected override void DoStuff() 
    { 
     // stuff 
    } 
} 
+0

我會在Parent的'DoStuff'調用'Init()'之前調用'Init()',因爲基類仍然沒有意識到需要執行初始化操作(除非所有子類最終結束)要求它......) –

+0

但父母的DoStuff是抽象的,實際上並沒有一種方法。 DoStuff和Init都在孩子身上。我同意,如果有些孩子不需要調用Init,那麼這個解決方案會變得很麻煩。 – taylonr

+2

-1您是否看到類層次結構和構造函數初始化本身包含當前實現中的DoStuff()和Init()?在這種情況下,這是無用的代碼片段 –

14

如果您對您構建一個對象複雜的邏輯,然後再考慮FactoryMethod模式。

在你的情況我會實現它作爲一個簡單的

public static Parent Construct(someParam) 

方法需要一些參數,並在此基礎上決定實例化哪個子類。 您可以從構造函數中刪除DoStuff()方法調用,並在新實例的Construct()中調用它。

此外,您應該避免在構造函數中調用虛擬/抽象方法。看到這個問題的更多細節:Virtual member call in a constructor

1

更正:每this answer作爲,你不能確定建設的子類時的基類的構造函數被調用時。

E.g.這不起作用:

所以,是的,正如其他人所指出的,如果事件的順序很重要,那麼基類需要能夠通過聲明,以抽象的方式,或以適應(更好的)通過讓子類實現DoStuff代表事件序列:

protected override void DoStuff() 
{ 
    Init(); 
    base.DoStuff(); 
} 
+0

In * C#*?我知道你可以在C++中做這些事情。 –

+0

-1 - 構造函數體中的base()不會編譯 –

+0

通過替代方法確認並更正。 –

1

DoStuff是抽象的。只需調用DoStuff頂部的Init即可。

+0

對於我的場景,需要在每個子項的DoStuff()重寫中調用Init(),這並不比在每個子項的構造函數中放置DoStuff()更好。另外,Init()可能需要用於其他方法,因此需要在構建中的某個地方完成。 – asmo

+1

這使得泰勒的解決方案更加可行。 –

+0

+1他和你的都很好。 – hoodaticus

0

而不是使用abstract方法,它會要求你實現所有子類的方法,你可以嘗試:

public class Parent 
{ 
    public Parent() 
    { 
     PostConstructor(); 
    } 


    protected virtual void PostConstructor() 
    { 
    } 
} 

public class Child : Parent 
{ 
    protected override void PostConstructor() 
    { 
     base.PostConstructor(); 

     /// do whatever initialization here that you require 
    } 
} 

public class ChildWithoutOverride 
{ 
    /// not necessary to override PostConstructor 
} 
+0

K.I.S.S.你;) –

+0

@Artur?這很簡單。 –

+0

是的+1優雅,我是K.I.S.S. apologet。否則即使很小的框架也很容易變得怪異。 –

4

我會強烈建議使用工廠像一個模式。

如果這是可能的:

。將所有孩子和抽象類別推入單獨的程序集

。像內部方法一樣聲明孩子的名字,所以沒有人可以通過調用ctor來構造它們。

。實現Factory類來構造調用者指定的對象類型,在實際創建一個對象之後,但在將它返回給調用者之前,這種類型將會抽象調用抽象的DoStuff()方法。

好東西關於這個:它會給你更多的抽象層次,所以如果將來你需要一些更多的函數調用或任何其他類型的邏輯複雜度,你將需要的只是將它們添加到您的Factory類中。

也就是說。

問候

+0

獨立程序集不可能的想法:對於DEBUG編譯,在基本ctor中有一個方法來查看堆棧並確保它是由工廠方法調用的。 –

+0

另一個替代想法:將子類移動到它自己的名稱空間,可能帶有一個名稱,例如MyNamespace.DoNotUseThisUseFactoryMethodInstead,所以您不使用...;它並且意外地使用小孩。 –

0

在WPF應用程序,你可以用Dispatcher幫助推遲DoStuff()的invokation:

abstract class Parent 
{ 
    public Parent() 
    { 
     Dispatcher.CurrentDispatcher.BeginInvoke(new Action(this.DoStuff)); 
    } 

    private void DoStuff() 
    { 
     // stuff, could also be abstract or virtual 
    } 
} 

但是,它不能保證DoStuff()將構造後立即調用。

2

正如其他人所說,你應該使用工廠模式。

public class Parent 
{ 
    public Parent() 
    {    
    } 

    public virtual void PostConstructor() 
    { 
    } 
} 

public class Child : Parent 
{ 
    public override void PostConstructor() 
    { 
     base.PostConstructor(); 

     // Your code here 
    } 
} 

public void FactoryMethod<T>() where T : Parent 
{ 
    T newobject = new T(); 
    newobject.PostConstructor(); 
} 
0

讓我來介紹一個使用一些C#特性的通用解決方案。請注意,此解決方案不需要在構造對象後使用工廠模式或調用任何模式,並且它僅適用於任何類,只需使用單一方法實現接口即可。 首先聲明我們的類將有一個接口來實現:

public interface IInitialize { 
    void OnInitialize(); 
} 

接下來我們添加靜態擴展類此接口,並添加初始化方法:

public static class InitializeExtensions 
{ 
    public static void Initialize<T>(this T obj) where T: IInitialize 
    { 
     if (obj.GetType() == typeof(T))  
      obj.OnInitialize(); 
    } 
} 

現在,如果我們需要一個類及其所有後代在對象完全構建後立即調用初始化方法,我們需要做的就是執行IInitialize並在構造函數中追加一行:

public class Parent : IInitialize 
{ 
    public virtual void OnInitialize() 
    { 
     Console.WriteLine("Parent"); 
    } 

    public Parent() 
    { 
     this.Initialize(); 
    } 
} 

public class Child : Parent 
{ 
    public Child() 
    { 
     this.Initialize(); 
    } 

    public override void OnInitialize() 
    { 
     Console.WriteLine("Child"); 
    } 
} 

public class GrandChild : Child 
{ 
    public GrandChild() 
    { 
     this.Initialize(); 
    } 

    public override void OnInitialize() 
    { 
     Console.WriteLine("GrandChild"); 
    } 
} 

訣竅是當一個派生類調用擴展方法Initialize時,這將抑制任何不是來自實際類的調用。