在C#里,访问修饰符决定了类成员能被哪些代码看到和使用。protected和internal是其中两个容易混淆的修饰符,它们控制的可见性范围完全不同,但经常会被组合使用。

基本含义
protected修饰的成员只能在声明它的类内部以及从该类型派生的子类中访问,不管子类在哪个程序集。internal修饰的成员则只能在同一个程序集(通常是同一个DLL或EXE)中的任何代码访问,和继承关系无关。
单独使用时的区别
下面用两个简单类来说明。
// 程序集 A 中
public class BaseClass
{
protected int protectedField = 1;
internal int internalField = 2;
}
public class DerivedClass : BaseClass
{
public void Test()
{
// 可以访问,因为是派生类
int a = protectedField;
// 可以访问,因为同在程序集 A
int b = internalField;
}
}
public class SameAssemblyOther
{
public void Test()
{
BaseClass b = new BaseClass();
// 错误:protectedField 不可访问
// int x = b.protectedField;
// 可以访问,因为同在程序集 A
int y = b.internalField;
}
}
如果有一个位于其他程序集的类继承BaseClass,它只能碰protected成员,internal成员对它不可见。
// 程序集 B 中
public class ExternalDerived : BaseClass
{
public void Test()
{
// 可以访问,跨程序集但属于派生类
int a = protectedField;
// 错误:internal成员不在同一程序集
// int b = internalField;
}
}
组合修饰符
C#还允许把两者组合,形成不同的语义:
- protected internal:同一程序集内任意代码,或任意程序集中的派生类可访问(取并集)。
- private protected:只能是同一程序集中的派生类可访问(取交集)。
对照表
| 修饰符 | 同一程序集非派生 | 同一程序集派生 | 其他程序集派生 | 其他程序集非派生 |
|---|---|---|---|---|
| protected | 否 | 是 | 是 | 否 |
| internal | 是 | 是 | 否 | 否 |
| protected internal | 是 | 是 | 是 | 否 |
| private protected | 否 | 是 | 否 | 否 |
实际使用建议
当你希望基类向子类开放某些能力但不想让外部随意调用时,用protected。当你在写一个类库,只想让同项目内的类型协作而不暴露给引用该程序集的人,用internal。组合形式则适合更精细的封装需求。
注意:访问修饰符只限制编译期访问,运行期反射可以突破这些限制,但这不属于正常编码范畴。
理解这些区别能帮你在设计类型时少踩坑,也让代码边界更清晰。