我有一个包含串口的设备的抽象类,衍生类也使用串口,但我不想让串口在这些衍生类之外可见。
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: 不要声明可见的实例字段。,它说字段应该是一个隐藏的实现细节。 好吧,我可以使用一个受保护的属性来代替,保护我的类不被破坏。
但它继续推荐字段是私有的 或内部. 该 C#编程指南的访问修饰符页面 说,将串口设置为 "protected "允许它只在派生类中被访问,而将它设置为 "internal "则允许它在整个汇编中被访问。 那么为什么 "内部 "可以接受,而 "受保护 "就不行呢? 受保护的访问不是比内部访问更不明显吗?
相关的问题。 抽象类中的字段的可访问性应该是多少?
为了实现封装,我们不应该将我们的字段直接暴露给外部,甚至是暴露给自己汇编内部的一个类。
相反,我们可以有一个内部的getter方法,这样只有我们汇编内部的类才能访问它,使用它,但不能改变它的引用。
如果我们再进一步,希望abstractbase类只能由我们自己的汇编中的类来扩展。
通过将基类公开,并指定所有构造函数为内部构造函数 我们可以确保只有我们自己的汇编中的类可以扩展它。
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.");
}
}
从某种程度上来说,protected比internal更明显,因为如果你扩展了这个类,你可以在汇编之外访问这个字段,但是internal却将它限制在定义了字段的一个汇编中。
Oguz的答案很好,但我决定使用一个属性而不是方法。 所以我的解决方案是这样的,并且似乎符合最佳实践。
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.");
}
}