3

我正在阅读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;
        }
    }
}

书中的解释:请注意,我已使用 this(“默认名称”)来确保 Name 在此构造函数被调用时获得一个值,如果该类用于派生一个新类,则这是可能的。这是必要的,因为不为 Name 字段分配值可能会导致以后出现错误。

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

4

3 回答 3

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

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

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

例如给定上面的类定义,那么以下两个类声明都会导致编译错误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);

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

于 2013-07-07T19:05:15.037 回答
0

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

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

于 2013-07-07T19:11:16.430 回答
0

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

它是调用另一个默认构造函数的原因只是一种在用户不知道/关心它是什么的情况下拥有硬编码默认值的方式。这类似于 .NET 4.0 中函数的默认参数。

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


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

public class OuterClass
{
    private OuterClass() {  }

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

这样,您只能从静态方法中创建类的新实例。

于 2013-07-07T18:35:16.237 回答