C#中的私有构造函数

Jua*_*rta 3 c# constructor private

我正在阅读Beginning 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

书中的解释:请注意,我已经使用它("默认名称")来确保如果调用此构造函数,Name将获取一个值,如果使用此类来派生新类,则可以使用该值.这是必要的,因为不为Name字段赋值可能会在以后出现错误.

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

Mat*_*son 9

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

你是对的,感到困惑!

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

在示例代码,对象也不会得到"默认名称"作为它的值.

所以这是书中的错字或错误.

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

  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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

另请注意,如果在基类中声明非默认构造函数并且根本不声明默认构造函数,则任何派生类都必须调用现有基类构造函数之一.

例如,给定上面的类定义,以下两个类声明都将导致编译错误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.
}
Run Code Online (Sandbox Code Playgroud)

要修复错误,MyDerivedClass必须调用现有的构造函数:

class MyDerivedClass: MyClass
{
    public MyDerivedClass(): base("My new name")
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

那么有什么用一个私有构造

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

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

通常,您可以使用私有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
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

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

OuterClass test = new OuterClass("Test");
Console.WriteLine(test.GetInnerClass().Value);
Run Code Online (Sandbox Code Playgroud)

就个人而言,我从来没有必要编写一个从其包含类派生的嵌套类,但如果你因某些原因需要这样做,它是可能的.