为什么我们需要私有构造函数?

79
如果一个类有私有构造函数,则无法实例化该类。因此,如果我不想让我的类被实例化,仍然要使用它,那么我可以将其设置为静态的。
私有构造函数的用途是什么?
除了单例模式之外,还有其他用途吗?
(注意:上面排除单例情况的原因是我不理解为什么需要单例,当有静态类可用时。您不必回答我在问题中的困惑。)
10个回答

80

工厂模式

私有构造函数在使用工厂模式时非常有用(也就是说,使用静态函数获取类的实例而不是显式实例化)。

public class MyClass
{ 
    private static Dictionary<object, MyClass> cache = 
        new Dictionary<object, MyClass>();

    private MyClass() { }

    public static MyClass GetInstance(object data)
    {
        MyClass output;

        if(!cache.TryGetValue(data, out output)) 
            cache.Add(data, output = new MyClass());

        return output;           
    }
}

伪封装与嵌套子类

任何继承自外部类的嵌套类都可以访问私有构造函数。

例如,您可以使用此功能创建一个抽象类, 您可以从中继承,但其他人不能(使用internal构造函数也可以在这里限制继承到单个程序集,但private构造函数强制所有实现都是嵌套类)。

public abstract class BaseClass
{
    private BaseClass() { }

    public class SubClass1 : BaseClass
    {
        public SubClass1() : base() { }
    }

    public class SubClass2 : BaseClass
    {
        public SubClass2() : base() { }
    }
}

基础构造函数

它们也可以用于创建“基础”构造函数,这些构造函数从不同的、更易访问的构造函数中调用。

public class MyClass
{
    private MyClass(object data1, string data2) { }

    public MyClass(object data1) : this(data1, null) { }

    public MyClass(string data2) : this(null, data2) { }

    public MyClass() : this(null, null) { }
}

我喜欢缓存添加语句中的赋值语句,我曾经使用过这种语法来处理延迟加载属性和空合并运算符,但之前从未想到它同样可以在常规方法中使用。 - Chris Marisic
1
第一个示例似乎是SingletonFactory的一个例子? - Ankush Jain
最后一个案例的模式不需要是“private”,但是“private”很好,因为没有人可以从外部使用该构造函数。我已经使用它来为每个解析为相同中央代码的构造函数制作略有不同的行为。(例如:public MyClass(string data2) : this(null, "Hello " + data2) { })此外,使用C#6的{get;}属性,赋值代码仅在任何构造函数区域内有效,因此它们倾向于在那个“private”构造函数中。 - 5argon

40
正如Stefan、Adam和其他人所指出的,私有构造函数在某些情况下非常有用,比如不希望类被类外代码创建。单例模式、工厂模式、静态方法对象都是限制类型构建的有用示例。
回答你关于为什么需要单例模式而静态类存在的问题:单例模式和静态类并不等同。例如,单例类可以实现接口,而静态类则不能;单例对象可以作为参数传递到方法中,而静态类则需要使用包装对象或反射来实现;还有一些情况需要创建继承层次结构,其中一个(或多个)叶子类是单例 - 这在静态类中也不可能实现。另外,你可能有几个不同的单例对象,并希望根据环境或配置参数在运行时实例化其中之一,这也无法通过静态类实现。
重要的是要理解语言特性并选择正确的特性 - 它们存在是有原因的。

单例模式与静态类的区别讲解得非常好!我从中学到了很多!顺便说一下,你实际上可以使用“静态初始化”来初始化一个非静态的单例类。请参阅官方文档。这是另一个让你在开始时感到困惑,最终学到好东西的概念。 :-) - RayLuo

15
有时候你不应该能够实例化一个类。这使得这一点显式,并在编译器层面强制执行。
单例模式只是其中一种用例。常量类、静态方法类和其他类型的模式决定了一个类不应该被实例化。

1
仅供完整性参考:可以将一个类标记为静态,这样确保只允许静态成员,并且该类无法被实例化。 - flq

6

在一个类中创建私有构造函数的目的

  1. To restrict a class being inherited.

  2. Restrict a class being instantiate or creating multiple instance/object.

  3. To achieve the singleton design pattern.

    public class TestPrivateConstructor
    {
        private TestPrivateConstructor()
        {  }
    
        public static int sum(int a , int b)
        {
            return a + b;
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            // calling the private constructor using class name directly 
            int result = TestPrivateConstructor.sum(10, 15);
            // TestPrivateConstructor objClass = new TestPrivateConstructor(); // Will throw the error. We cann't create object of this class
        }
    }
    

谢谢您指出这一点。没有人指出它会限制一个类被继承。 - Basanta Matia

4
你可以使用它来强制使用singleton实例或创建工厂类。
静态方法可以调用私有构造函数来创建该类的新实例。
例如,单例实例:
public class Foo
{

  private Foo (){}

  private Foo FooInstance {get;set;}

  public static Foo GetFooInstance ()
  {
    if(FooInstance == null){
      FooInstance = new Foo();
    }

    return FooInstance;
  }

}

这允许仅创建该类的一个实例。

2

如果您的唯一目标是不希望它被实例化,那么将其设为静态即可。

如果您只是不希望它在类外被实例化(也许您只想用户通过类上的静态工厂获取一个实例),那么您需要一个私有构造函数来允许公开访问的静态工厂实例化它。

历史上,要使一个类不可实例化并没有总是使用静态关键字... 在静态关键字应用于类之前,将构造函数设为私有是一种方法来使它不可实例化。


1
关于单例模式 - 单例是一种设计模式,当环境和需求满足模式使用的类似动机时使用;静态类是一种语言特性。
正如LBushkin's answer所讨论的那样,虽然使用静态类可以实现使用单例的某些目标,但是单例的特定实现可能超出了仅使用静态类的功能集。

0

私有构造函数是一种特殊的实例构造函数。在某些情况下,我们创建一个只有静态成员的类,因此创建该类的实例是无用的,这就是私有构造函数发挥作用的地方。

如果一个类有一个或多个私有构造函数且没有公共构造函数,则其他类(除嵌套类外)无法创建该类的实例。

例如:

class LogClass {

  public static double e = Math.E;  //2.71828

  // Private Constructor:
  private LogClass() { 

 }  
}

空构造函数的声明会阻止自动生成无参构造函数。

如果构造函数没有使用访问修饰符,则默认为私有。 阅读更多:https://qawithexperts.com/tutorial/c-sharp/32/private-constructor-in-c-sharp


0

如果一个类只有私有构造函数,那么它就无法从外部实例化。

你也可以拥有不同签名的私有构造函数和公共构造函数。


0
如果您想为一个类创建一个工厂,可以使用私有构造函数,并在类本身中添加一些静态的“工厂”方法来创建该类。
其中一个例子是Graphics类,它具有From*方法。

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