C#中的私有构造函数

本文关键字:构造函数 | 更新日期: 2023-09-27 18:21:17

我正在阅读开始Visual C#2012

考虑:

using System;
using System.Collections.Generic;
using System.Text;
namespace Ch10Ex01
{
    class MyClass
    {
        public readonly string Name;
        private int intVal;
        public int Val
        {
            get { return intVal; }
            set
            {
                if (0 <= value && value <= 10)
                    intVal = value;
                else
                    throw (new ArgumentOutOfRangeException("Val", value,
                        "Val must be assigned a value between 0 and 10."));
            }
        }
        public override string ToString()
        {
            return "Name: " + Name + "'nVal: " + Val;
        }
        private MyClass() : this("Default Name")
        {
        }
        public MyClass(string newName)
        {
            Name = newName;
            intVal = 0;
        }
    }
}

书中的解释:请注意,我已经使用了这个("默认名称")来确保如果调用这个构造函数,Name会得到一个值,如果这个类用于派生一个新的类,这是可能的。这是必要的,因为不为"名称"字段赋值可能会导致以后出现错误。

让我困惑的是:它是"私有"的,怎么能在派生类中使用?这("默认名称")是什么意思?对象如何获得"默认名称"作为其名称?

C#中的私有构造函数

让我困惑的是:它是"私有"的,怎么能在派生类中使用?这("默认名称")是什么意思**对象如何获得"默认名称"作为其名称?

你感到困惑是对的!

该代码示例根本不调用默认构造函数,而且因为它是私有的,所以其他任何东西都不能在不使用反射的情况下调用它(甚至不是派生类;派生类必须至少为protected才能调用它,或者派生类必须嵌套在基类中)。

在示例代码中,对象不会获取"Default Name"作为其值。

所以这是书中的一个打字错误或错误。

本书描述的正确解决方案是:

  1. 完全省略默认构造函数
  2. 在现场范围内初始化Name。这确保了初始化失败是不可能的,无论在这个或任何派生类中编写了什么其他构造函数

像这样:

class MyClass
{
    public readonly string Name = "Default Name";
    private int intVal;
    public int Val
    {
        get
        {
            return intVal;
        }
        set
        {
            if (0 <= value && value <= 10)
                intVal = value;
            else
                throw (new ArgumentOutOfRangeException("Val", value,
                    "Val must be assigned a value between 0 and 10."));
        }
    }
    public override string ToString()
    {
        return "Name: " + Name + "'nVal: " + Val;
    }
    public MyClass(string newName)
    {
        Name = newName;
        intVal = 0;
    }
}

请注意,声明一个由其他构造函数调用的私有默认构造函数通常很有用,但声明类必须实际使用它

还要注意,如果在基类中声明非默认构造函数,而根本不声明默认构造函数,则任何派生类都必须调用现有基类构造函数之一。

例如,给定上面的类定义,那么以下两个类声明都将导致编译错误MyClass' does not contain a constructor that takes 0 arguments:

class MyDerivedClass1: MyClass
{
    public MyDerivedClass1()  // Compile error
    {
    }
}
class MyDerivedClass2: MyClass
{
    // No constructor declared at all. Also a compile error.
}

为了修复错误,MyDerivedClass必须调用一个现有的构造函数:

class MyDerivedClass: MyClass
{
    public MyDerivedClass(): base("My new name")
    {
    }
}

所以使用的是私有构造函数

一个相当典型的用法是将常见的初始化代码放入默认构造函数中。然而,有时您不希望调用方能够默认构造类型——在这种情况下,您可以将默认构造函数设为私有构造函数。

这样,您仍然可以使用默认构造函数进行常见初始化,但您可以阻止类外的代码进行初始化,例如:

class Test
{
    public readonly int IntValue;
    public readonly string StringValue;
    private Test()
    {
        // Do common initialisation.
    }
    public Test(int intValue): this()
    {
        IntValue = intValue;
    }
    public Test(string stringValue): this()
    {
        StringValue = stringValue;
    }
}

通常,您可以只使用专用init()方法来进行常见初始化,但如果您正在初始化readonly字段,则必须使用构造函数来进行初始化。在这种情况下,必须使用专用构造函数而不是init()方法。

私有默认构造函数的另一个用途是完全防止该类型的任何实例化(您只声明了一个私有默认构造函数,而根本没有其他构造函数)。

在.Net 1.x中,这是唯一的方法,但.Net的后续版本引入了静态类,在大多数情况下,这些类不需要为该类型使用私有构造函数。

您还可以声明一个私有构造函数来强制使用静态工厂方法来实例化类型。


为了完整起见,这里有一个人为的例子,演示了如何从嵌套派生类调用私有构造函数

class OuterClass
{
    public readonly string Value;
    private OuterClass(): this("Default Value")
    {
    }
    public OuterClass(string value)
    {
        Value = value;
    }
    public OuterClass GetInnerClass()
    {
        return new InnerClass();
    }
    private class InnerClass: OuterClass
    {
    }
}

使用该类定义,以下代码将打印"默认值":

OuterClass test = new OuterClass("Test");
Console.WriteLine(test.GetInnerClass().Value);

就我个人而言,我从来没有写过从包含类派生的嵌套类,但如果出于某种原因需要这样做,这是可能的。

因为这个类有一个private构造函数,所以调用构造函数的唯一方法是在这个特定的类中。即使是派生类也不能调用它(如果它是protected,那么它可以)。正如您的代码示例所示,这毫无用处。

它之所以是一个调用另一个的默认构造函数,只是为了在用户不知道/不关心它是什么的情况下获得一个硬编码的默认值。这类似于.NET 4.0中函数的默认参数。

对我来说,将这两个构造函数公开会更有意义,因为我没有看到任何使用默认构造函数的代码(如果你已经向我们展示了所有代码)。


使用私有构造函数有一些合理的理由,特别是使用静态函数或子类。以以下为例:

public class OuterClass
{
    private OuterClass() {  }
    public static OuterClass GetOuter() { return new OuterClass(); }   
}

通过这种方式,您只能从静态方法中创建类的新实例。

私有构造函数意味着用户不能直接实例化类。相反,您可以使用类似于命名构造函数Idiom的东西来创建对象,其中您有可以创建和返回类实例的静态类函数。您可以使用私有构造函数来控制通常在singleton模式中使用的Object的实例化。

您必须阅读singleton模式才能理解私有构造函数的用途。