C#中的私有构造函数

时间:2013-07-07 18:25:25

标签: 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;
        }
    }
}

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

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

3 个答案:

答案 0 :(得分:9)

  

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

你感到困惑是对的!

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

在示例代码中,对象获取“默认名称”作为其值。

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

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

  1. 完全省略默认构造函数。
  2. 在字段范围内初始化Name。这可以确保无论是在这个或任何派生类中编写其他构造函数,都无法初始化它。
  3. 像这样:

    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);
    

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

答案 1 :(得分:0)

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

它是一个默认构造函数调用另一个的原因只是一种硬编码默认值的方法,而不需要用户知道/关心它是什么。这类似于.NET 4.0中函数的默认参数。

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


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

public class OuterClass
{
    private OuterClass() {  }

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

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

答案 2 :(得分:0)

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

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