不只是Public与Private:C#访问修饰符全方位解读

一、访问修饰符的控制范围

1.public:完全公开

  • 访问范围 :无限制。任何代码,无论是在同一个程序集(Assembly)还是在其他程序集中,都可以访问被public修饰的成员。
  • 适用对象:类、结构、接口、枚举、委托、以及类的成员(方法、属性、字段等)。
  • 用途:用于定义一个类的"公共API"。这些是你希望提供给外部世界使用的功能。
csharp 复制代码
// 在 AssemblyA.dll 中
public class Calculator
{
    public int Add(int a, int b) // 任何代码都可以调用这个方法
    {
        return a + b;
    }
}
csharp 复制代码
// 在另一个项目 AssemblyB.exe 中,引用了 AssemblyA.dll
using AssemblyA;

class Program
{
    static void Main(string[] args)
    {
        Calculator calc = new Calculator();
        int result = calc.Add(2, 3); // 可以访问
        Console.WriteLine(result);
    }
}

2.private:绝对私有

  • 访问范围 :仅限在声明它的类或结构内部。
  • 适用对象 :类的成员(嵌套类、方法、属性、字段等)。注意:顶层类型(非嵌套类)不能是private的。
  • 用途 :封装的基石。用于隐藏类的内部状态和实现细节。一个类中绝大部分的字段都应该是private的。
csharp 复制代码
public class BankAccount
{
    private decimal _balance; // 只能在BankAccount类内部访问

    public void Deposit(decimal amount)
    {
        if (amount > 0)
        {
            _balance += amount; // 内部可以访问
        }
    }
}

class Test
{
    void DoSomething()
    {
        BankAccount account = new BankAccount();
        // account._balance = 10000; // 编译错误!无法从外部访问_balance
    }
}

3.protected:家族内部的秘密

  • 访问范围 :在声明它的内部,以及**该类的任何子类(派生类)**中。
  • 适用对象:类的成员。
  • 用途:为了让子类能够访问和扩展父类的功能,同时又不想让这些功能对外界完全公开。它在继承体系中扮演着关键角色。
csharp 复制代码
// 在 AssemblyA.dll 中
public class Animal
{
    protected string Name { get; set; } // 子类可以访问Name

    protected void Eat()
    {
        Console.WriteLine($"{Name} is eating.");
    }
}

public class Dog : Animal
{
    public Dog(string name)
    {
        this.Name = name; // 正确:子类可以访问受保护的成员
    }

    public void Bark()
    {
        Console.WriteLine("Woof!");
        this.Eat(); // 正确:子类可以调用受保护的方法
    }
}
csharp 复制代码
// 在 AssemblyA.dll 或其他程序集中
class Test
{
    void DoSomething()
    {
        Animal animal = new Animal();
        // animal.Name = "Tom"; // 编译错误!外部无法访问protected成员
        // animal.Eat();      // 编译错误!

        Dog dog = new Dog("Buddy");
        // dog.Name = "Lucy"; // 编译错误!即使通过子类实例,外部也无法访问
    }
}

关键点protected的访问权限是基于继承关系 的,而不是实例。在Test类中,即使有一个Dog的实例,也不能访问其protected成员,因为Test类不是Animal的子类。

4. nternal:同一个"项目"里的朋友

  • 访问范围:仅限在同一个**程序集(Assembly)**内部。一个程序集通常对应一个项目(.csproj),编译后生成一个DLL或EXE文件。
  • 适用对象:顶层类型和类的成员。
  • 用途 :当你想在同一个项目或组件内部共享一些类或工具方法,但又不希望将它们暴露给引用这个组件的外部项目时,internal是最佳选择。这是创建框架或库时非常有用的一个修饰符。
csharp 复制代码
// 在 AssemblyA.dll 中
internal class InternalHelper
{
    public static void DoWork()
    {
        Console.WriteLine("Internal work done.");
    }
}

public class PublicFacade
{
    public void PerformAction()
    {
        InternalHelper.DoWork(); // 同一个程序集内,可以访问internal类
    }
}
csharp 复制代码
// 在另一个项目 AssemblyB.exe 中,引用了 AssemblyA.dll
using AssemblyA;

class Program
{
    static void Main(string[] args)
    {
        PublicFacade facade = new PublicFacade();
        facade.PerformAction(); // 可以调用

        // InternalHelper helper = new InternalHelper(); // 编译错误!InternalHelper在AssemblyB中不可见
    }
}

默认访问级别 :如果你在声明一个顶层类(非嵌套)时不写任何访问修饰符 ,它的默认访问级别就是internal

csharp 复制代码
// 等同于 internal class MyClass { }
class MyClass { } 

5. 两种组合修饰符:更精细的控制

C#还提供了两种由protectedinternal组合而成的修饰符,用于实现更复杂的访问控制场景。

5.1 protected internal:家族朋友

  • 访问范围 :满足以下任一条件 即可访问:
    1. 在同一个程序集内。
    2. 在不同的程序集中,但必须是该类的子类。
  • 逻辑关系protected OR internal。这是最宽松的访问级别之一(仅次于public)。
  • 用途:当你希望一个成员主要在程序集内部使用,但同时也允许其他程序集中的子类对其进行扩展时。
csharp 复制代码
// 在 AssemblyA.dll 中
public class BaseClass
{
    protected internal int Value { get; set; }
}

public class DerivedInSameAssembly : BaseClass
{
    void Test()
    {
        this.Value = 10; // 可以访问,因为在同一个程序集
    }
}

internal class OtherClassInSameAssembly
{
    void Test()
    {
        BaseClass b = new BaseClass();
        b.Value = 20; // 可以访问,因为在同一个程序集
    }
}
csharp 复制代码
// 在 AssemblyB.exe 中,引用了 AssemblyA.dll
using AssemblyA;

public class DerivedInOtherAssembly : BaseClass
{
    void Test()
    {
        this.Value = 30; // 可以访问,因为是子类
    }
}

class OtherClassInOtherAssembly
{
    void Test()
    {
        BaseClass b = new BaseClass();
        // b.Value = 40; // 编译错误!不在同一程序集,也不是子类
    }
}

5.2 private protected:本家的私密

  • 访问范围 :必须同时满足 以下两个条件才能访问:
    1. 在同一个程序集内。
    2. 必须是该类的子类。
  • 逻辑关系private AND protected(概念上),或者更准确地说是protected AND internal
  • 用途 :这是最严格的访问级别之一。当你希望某个成员只能被同一个项目中的子类访问时使用。这可以防止其他不相关的项目继承你的类并滥用这些受保护的成员。
csharp 复制代码
// 在 AssemblyA.dll 中
public class BaseClass
{
    private protected string SecretCode { get; set; }
}

public class DerivedInSameAssembly : BaseClass
{
    void Test()
    {
        this.SecretCode = "Alpha"; // 可以访问,同一程序集 + 子类
    }
}

internal class OtherClassInSameAssembly
{
    void Test()
    {
        BaseClass b = new BaseClass();
        // b.SecretCode = "Beta"; // 编译错误!虽然在同一程序集,但不是子类
    }
}
csharp 复制代码
// 在 AssemblyB.exe 中,引用了 AssemblyA.dll
using AssemblyA;

public class DerivedInOtherAssembly : BaseClass
{
    void Test()
    {
        // this.SecretCode = "Gamma"; // 编译错误!虽然是子类,但不在同一个程序集
    }
}

三、访问修饰符总结与对比

修饰符 当前类内部 同程序集内的子类 同程序集内的非子类 不同程序集的子类 不同程序集的非子类
public
protected internal
internal
protected
private protected
private

结语

点个赞,关注我获取更多实用 C# 技术干货!如果觉得有用,记得收藏本文

相关推荐
用户69371750013841 小时前
Google 正在“收紧侧加载”:陌生 APK 安装或需等待 24 小时
android·前端
蓝帆傲亦2 小时前
Web 前端搜索文字高亮实现方法汇总
前端
用户69371750013842 小时前
Room 3.0:这次不是升级,是重来
android·前端·google
似水明俊德3 小时前
02-C#.Net-反射-面试题
开发语言·面试·职场和发展·c#·.net
漫随流水3 小时前
旅游推荐系统(view.py)
前端·数据库·python·旅游
踩着两条虫4 小时前
VTJ.PRO 核心架构全公开!从设计稿到代码,揭秘AI智能体如何“听懂人话”
前端·vue.js·ai编程
阿蒙Amon4 小时前
C#常用类库-详解SerialPort
开发语言·c#
jzlhll1235 小时前
kotlin Flow first() last()总结
开发语言·前端·kotlin
似水明俊德6 小时前
02-C#.Net-反射-学习笔记
开发语言·笔记·学习·c#·.net
蓝冰凌6 小时前
Vue 3 中 defineExpose 的行为【defineExpose暴露ref变量】详解:自动解包、响应性与实际使用
前端·javascript·vue.js