2011-11-03 67 views
15

我希望能够在构建派生对象时自动调用特定的方法,但我无法考虑如何去做。以下代码说明。另一个答案建议OnLoad,但我这样做的Mac上的统一和OnLoad似乎不支持我的平台。有什么建议么?有没有办法在所有构造函数运行后立即自动调用特定的方法?

public class Parent { 

    public Parent() 
    { 
     // A. Stuff to do before child constructor code runs 
     DoThisAutomaticallyAfterConstruction(); 
    } 

    public void DoThisAutomaticallyAfterConstruction() 
    { 
     // C. In this example, this will run after A, before B. I want it to run ABC 
    } 
} 

public class Child : Parent { 

    public Child() : base() 
    { 
     // B. Stuff to do here after parent constructor code runs 
    } 
} 
+2

这是代码味道给我。基类不应该与派生类有关。另外,构造器应该设置对象的初始化状态。 – Jason

+1

+1 @ jason - 我同意。 – JonH

回答

13

不幸的是,没有内置的方法来做你想做的事(这是一个相当常被要求的功能)。

一种解决方法是实现工厂模式,在这种模式中,您不通过直接调用构造函数来创建对象,而是实现静态方法来为您创建它们。例如:

public class MyClass 
{ 
    public MyClass() 
    { 
    // Don't call virtual methods here! 
    } 

    public virtual void Initialize() 
    { 
    // Do stuff -- but may be overridden by derived classes! 
    } 
} 

然后加入:

public static MyClass Create() 
{ 
    var result = new MyClass(); 

    // Safe to call a virtual method here 
    result.Initialize(); 

    // Now you can do any other post-constructor stuff 

    return result; 
} 

和与其做

var test = new MyClass(); 

你可以做

var test = MyClass.Create(); 
+0

啊,辉煌!我听说过工厂,但这有助于我更多地了解它们。 –

+0

@RobinKing查看我的编辑答案,获取关于工厂模式更多细节的几个指针。静态工厂方法是一种简单的方法,但有些更喜欢使用工厂对象(有些情况需要使用它们)。 – phoog

+1

这是一篇很老的文章,但将构造函数更改为'private'可能会很有用,这将强制客户端使用工厂。 –

2

基于你完成ACB你的榜样,你要完成ABC

为了在子构造函数之后运行代码,需要在B(子构造函数)之后调用不能调用A的代码(父构造函数),那么您将无法完成ABC。

在子类构造函数的末尾移动DoThisAutomaticallyAfterConstruction()

虽然是一个奇怪的问题。

+0

不幸的是,这并不一定在未被封装的类中实现,因为即使是更多派生的类也可以在子构造函数完成执行后进行进一步的初始化,所以在虚函数中调用虚拟成员仍然不安全。构造函数。 –

+1

@JeremyTodd - 如果情况确实如此,我们需要更多来自OP的信息。我正在回答这篇文章的信息。 – JonH

+0

在正常的代码中很奇怪,但是在处理UI代码时这通常是必需的。但正如作者所说,通常我们只会使用OnLoad事件。 –

4

这听起来很适合工厂。使所有构造函数都是私有的或受保护的,要求代码的消费者在需要实例的对象时调用工厂方法。在工厂方法中,您使用new运算符创建对象,然后在返回对象之前调用DoThisAutomaticallyAfterConstruction()

EDIT

甲工厂可以是静态方法,也可以具有一个工厂对象。例如,请参阅关于抽象工厂模式的维基百科http://en.wikipedia.org/wiki/Abstract_factory_pattern以及ADO.NET DbProviderFactory的文档http://msdn.microsoft.com/en-us/library/wda6c36e.aspx以了解真实世界的实现。

+0

完美的感觉。谢谢! –

0

虽然@Jeremy托德(接受)答案作品并且是该问题广泛接受的解决方案,它有一个缺点:不是很好的IoC和序列化友好,因为你的课堂不能使用new正确构建。让我介绍一些使用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时,这将抑制任何不是来自实际类的调用。

相关问题