如何在C#中防止继承者调用基础构造函数?

31

我有一个(写得不好的)基类,我想要将其包装在代理对象中。该基类类似于以下内容:

public class BaseClass : SomeOtherBase 
{
   public BaseClass() {}

   public BaseClass(int someValue) {}

   //...more code, not important here
}

并且,我的代理类似于:

public BaseClassProxy : BaseClass
{
  public BaseClassProxy(bool fakeOut){}
}

没有“fakeOut”构造函数,将会调用基础构造函数。然而,如果有了它,我希望不会调用基础构造函数。无论如何,我需要一种方法来不调用任何基类构造函数,或者其他有效地代理这个(邪恶的)类的方法。


public BaseClass() { if ( GetType() != typeof(BaseClass) ) return; ... } - user12031933
13个回答

68

有一种方式可以创建一个对象,而不需要调用任何实例构造函数。

在进行操作之前,请确保你非常确定要以这种方式执行。99%的情况下,这都是错误的解决方案。

以下是方法:

FormatterServices.GetUninitializedObject(typeof(MyClass));

调用它来代替对象的构造函数。它将创建并返回一个实例,而不调用任何构造函数或字段初始化程序。

在WCF中反序列化对象时,它使用此方法创建对象。当这种情况发生时,构造函数甚至字段初始化程序都不会运行。


3
好的,这解决了我的问题!当我在一个继承类上运行单元测试时,我试图避免调用父对象构造函数中的数据库查询。 - Tarka
2
FormatterServices.GetUninitializedObject(typeof(MyClass)); 仍然会调用静态构造函数。 - Paul Suart
@PaulSuart,说得好。我已经更新了我的答案以反映这一点。 - Neil
这是一种代码异味,即使在单元测试中也是如此。"针对接口编程,而不是实现",例如,如果您想要伪造一个DbContext,您应该使用IObjectContextAdapter或从自定义上下文中提取一个接口。 - Jacob Brewer
另一个不喜欢 WCF 的原因 ;) - Jacob Brewer
1
我使用这个方法在单元测试中创建一个虚拟的 WPF 窗口,避免了线程需要为 STA 的提示。 - Lee D

26

如果您没有显式调用基类中的任何构造函数,则会隐式调用无参构造函数。这是不可避免的,您无法在不调用构造函数的情况下实例化一个类。


4
实际上,如果您阅读我下面的评论,有一种方法,尽管在大多数情况下我不建议使用它。 - Neil
5
这个回答是完全错误的,特别是考虑到另一个回答的情况。 - Kobor42

6

至少需要调用一个构造函数。我所能想到的唯一解决方法是包含。将该类放在另一个类内部或引用另一个类。


这是让我获得最多结果的路径。谢谢! - casademora
我可以补充一下,使用包含是实现代理模式最典型的方式。通常你会显式地实例化代理对象,并使其实现与被代理对象相同的接口:MyInterface myProxy = new MyProxiedClass(new MyClass()); - jjmontes

5

我不相信你可以避免调用构造函数。但是你可以像这样做:

public class BaseClass : SomeOtherBase 
{
   public BaseClass() {}

   protected virtual void Setup()
   {
   }
}

public BaseClassProxy : BaseClass
{
   bool _fakeOut;
   protected BaseClassProxy(bool fakeOut)
   {
        _fakeOut = fakeOut;
        Setup();
   }

   public override void Setup()
   {
       if(_fakeOut)
       {
            base.Setup();
       }
       //Your other constructor code
   }
} 

5

如果你不想调用两个基类构造函数中的任何一个,那是无法实现的。

C# 类的构造函数必须调用基类的构造函数。如果没有显式地调用,base( ) 就会被隐含调用。在你的例子中,如果你没有指定要调用哪个基类构造函数,那么就相当于:

public BaseClassProxy : BaseClass
{
    public BaseClassProxy() : base() { }
}

如果你更喜欢使用另一个基类构造函数,则可以使用:

public BaseClassProxy : BaseClass
{
    public BaseClassProxy() : base(someIntValue) { }
}

无论哪种方式,其中之一将会被显式或隐式调用。

1

很抱歉,不使用基础调用构造函数不是一个选项。


1
创建BaseClassProxy对象时,它需要创建其基类的实例,因此您需要调用基类构造函数,您可以选择调用哪个构造函数,例如:
public BaseClassProxy (bool fakeOut) : base (10) {}

调用第二个构造函数而不是第一个。

0

好的,这里有一个丑陋的解决方案,用于解决一个类继承另一个类的构造函数,而我不想允许其中一些工作的问题。我希望避免在我的类中使用它,但是现在它在这里:

这是我的类构造函数:

public MyClass();
{
   throw new Exception("Error: Must call constructor with parameters.");
}

好了,现在你被警告它很丑。请不要抱怨!

我想强制从我的主构造函数中至少获取最小参数,而不允许继承的基本构造函数没有参数。

我也相信,如果您创建一个构造函数并且没有在其后放置: base(),那么它将不会调用基类构造函数。 如果您为基类中的所有构造函数创建构造函数,并在主类中为它们提供完全相同的参数,则它将不会通过。但是,如果基类中有很多构造函数,这可能会很繁琐!


0

我提供另一个简单的解决方案:

class parent
{
    public parent()
    {
        //code for all children

        if (this.GetType() == typeof(child1))
        {
            //code only for objects of class "child1"
        }
        else
        {
            //code for objects of other child classes
        }
    }
}

class child1 : parent
{
    public child1()
    {}
}

// class child2: parent ... child3 : parent ... e.t.c

0
构造函数本质上是公开的。不要使用一个构造函数来进行实例化,而是使用另一个私有的构造函数来进行对象的构建。因此,你可以创建一个没有参数的实例,并调用该函数来构建你的对象实例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接