为什么内部字段优于抽象 class 中的受保护字段?

Why are internal fields preferable to protected fields in an abstract class?

我有一个包含串行端口的设备的摘要class:

public abstract class SerialDevice
{
    // serial port (should this be protected, internal, or protected internal?)
    protected SerialPort _serialPort;

    // The serial port has some shared methods.
    public void Open()
    {
        _serialPort.Open();
    }
}

Derived classes 也使用串行端口,但我不希望端口在那些 derived classes 之外可见。

public class Widget : SerialDevice
{
    // The serial port has some widget-specific functionality.
    public void UniqueCommand()
    {
        _serialPort.WriteLine("Hello world.");
    }
}

当我编译时,我得到这个警告:CA1051: Do not declare visible instance fields,它说字段应该是一个隐藏的实现细节。好的,我可以使用受保护的 属性 来保护我的 classes 免受破坏内容的更改。

但它继续建议字段是私有的或内部的C# Programming Guide's page on Access Modifiers 表示使串行端口 "protected" 只允许在派生的 classes 中访问它,而使它成为 "internal" 将允许它在整个程序集中访问.那么为什么 "internal" 可以接受,而 "protected" 不可以呢?受保护的访问不是比内部访问更不可见吗?

相关问题:What should the accessablity of Fields in a Abstract Class be?

在某种程度上,protected 比 internal 更明显,因为如果您扩展 class,则可以访问程序集外部的字段,但 internal 将其限制在定义该字段的那个程序集中。

为了实现封装,我们不应该将我们的字段直接暴露给外部,即使是我们自己的程序集内的 class。

相反,我们可以有一个内部 getter 方法,这样只有我们程序集中的 classes 可以访问它,使用它,但不能更改它的引用。

如果我们更进一步,希望 abstract/base class 只能由我们自己的程序集中的 classes 扩展:

通过使用基础 class public 并将所有构造函数指定为内部构造函数,我们可以确保只有 class 在我们自己的程序集中可以扩展它。

public abstract class SerialDevice
{
    private SerialPort _serialPort;

    internal SerialPort GetSerialPort()
    {
        return _serialPort;
    }

    // We define our base class public,
    // but also define all the constructors internal
    // Therefore, no class outside our assembly extend it
    // but we still can expose sub classes (like Widget) to the outside
    internal SerialDevice()
    {

    }

    // The serial port has some shared methods.
    // These methods are still visible from the outside
    public void Open()
    {
        _serialPort.Open();
    }
}

而且,

public class Widget : SerialDevice
{
    // The serial port has some widget-specific functionality.
    public void UniqueCommand()
    {
        GetSerialPort().WriteLine("Hello world.");
    }
}

Oguz 的回答很好,但我决定使用 属性 而不是方法。所以我的解决方案看起来像这样,并且似乎符合最佳实践。

  • 摘要 class 是 public,因此程序集的其余部分可以看到它。
  • 抽象 class' 构造函数是内部的,因此不能在程序集外部继承。
  • 串口,"Port,"是一个属性,不是一个字段,所以以后可以在抽象class中改变它,而不会破坏任何派生的[=26] =]es.
  • 属性 "Port" 受到保护,因此只能在 class 派生自基数 class 的 class 中看到它。
public abstract class SerialDevice
{
    // Constructor
    internal SerialDevice() {}

    // serial port
    protected SerialPort Port { get; } = new SerialPort();

    // The serial port has some shared methods.
    public void Open()
    {
        Port.Open();
    }
}
public class Widget : SerialDevice
{
    // The serial port has some widget-specific functionality.
    public void UniqueCommand()
    {
        Port.WriteLine("Hello world.");
    }
}