【问题标题】:Why are internal fields preferable to protected fields in an abstract class?为什么内部字段比抽象类中的受保护字段更可取?
【发布时间】:2020-08-16 11:22:05
【问题描述】:

我有一个包含串行端口的设备的抽象类:

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

派生类也使用串行端口,但我不希望端口在这些派生类之外可见。

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,它表示字段应该是一个隐藏的实现细节。好的,我可以改用受保护的属性,并保护我的类免受破坏内容的更改。

但它继续推荐字段是私有的或内部的C# Programming Guide's page on Access Modifiers 表示,使串行端口“受保护”允许它只能在派生类中访问,而使其“内部”允许它在整个程序集中被访问。那么为什么“内部”可以接受,而“受保护”则不行呢?受保护的访问不是比内部访问更不可见吗?

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

【问题讨论】:

  • 你的类是公共的,有一个公共构造函数(因为你的问题没有显示构造函数,我们应该假设它有一个默认构造函数)所以任何程序集中的任何类都可以扩展你的类并可以访问受保护的成员。这就是它抱怨的原因。
  • @OguzOzgul 这很有意义。我无法将抽象类设为私有,否则会收到错误 CS1527(命名空间中定义的元素不能显式声明为私有、受保护、受保护内部或私有受保护)。应该是内部,还是其他?
  • 如果我把它 internal 我得到 CS0060(不一致的可访问性:基类 'SerialDevice' 比类 'Widget' 更难访问)。解决办法是什么?
  • 让 SerialDevice 是公开的,这就是我们向外部世界公开扩展我们内部抽象类或基类的内部类的方式。只需将其作为内部构造函数即可。保持 _serialPort 内部

标签: c# .net access-modifiers


【解决方案1】:

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

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

如果我们更进一步,希望抽象/基类只能由我们自己程序集中的类扩展:

通过公开基类,并将所有构造函数指定为内部构造函数,我们可以确保只有我们自己的程序集中的类可以扩展它。

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.");
    }
}

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      • 抽象类是公共的,因此程序集的其余部分可以看到它。
      • 抽象类的构造函数是内部的,因此不能在程序集之外继承。
      • 串行端口“Port”是一个属性,而不是一个字段,因此以后可以在抽象类中对其进行更改,而不会破坏任何派生类。
      • “端口”属性受到保护,因此只能在从基类派生的类中看到它。
      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.");
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-01-12
        • 1970-01-01
        • 2020-11-02
        • 1970-01-01
        • 2015-09-05
        • 1970-01-01
        • 2011-01-13
        • 2011-06-22
        相关资源
        最近更新 更多