首页 / 教程列表 / C#程序设计基础(入门篇) / 作用域和访问修饰符

作用域和访问修饰符

1808 更新于: 2021-08-27 读完约需 9 分钟

类、记录和结构的可访问性

直接在命名空间中声明的类、记录和结构(换句话说,不嵌套在其他类或结构中)可以是public,也可以是internal。如果没有指定访问修饰符,则默认为internal

有类成员相同,所有类型也都具有可访问性级别。该级别可以控制是否可以从你的程序集或其他程序集中的其他代码中使用它们。 可以使用以下访问修饰符在进行声明时指定类型(类,记录,结构)的可访问性:

  • public:同一程序集中的任何其他代码或引用该程序集的其他程序集都可以访问该类型或成员。
  • private:只有同一 class 或 struct 中的代码可以访问该类型或成员。
  • protected:只有同一 class 或者从该 class 派生的 class 中的代码可以访问该类型或成员。
  • internal:同一程序集中的任何代码都可以访问该类型或成员,但其他程序集中的代码不可以。
  • protected internal:该类型或成员可由对其进行声明的程序集或另一程序集中的派生 class 中的任何代码访问。
  • private protected:只有在其声明程序集内,通过相同 class 中的代码或派生自该 class 的类型,才能访问类型或成员。

类型可访问性摘要表

下表列出了不同调用方对不同访问级别的类型的可访问性,其中√表示可访问,×表示不可访问

调用方的位置 public protected internal protected internal private protected private
在类内
派生类(相同程序集) ×
非派生类(相同程序集) × × ×
派生类(不同程序集) × × ×
非派生类(不同程序集) × × × × ×

下面的示例演示如何在类型(类,结构,记录)上指定访问修饰符:

/// <summary>
/// 类
/// </summary>
public class ClassBicycle
{
    public void Pedal() { }
}
/// <summary>
/// 记录
/// </summary>
public class RecordBicycle
{
    public void Pedal() { }
}
/// <summary>
/// 结构
/// </summary>
public class StructBicycle
{
    public void Pedal() { }
}

完整示例:

using System;

namespace ConsoleApp1
{
    static class Program
    {
        private static void Main(string[] args)
        {
            var bicycle1 = new ClassBicycle();
            bicycle1.Pedal();
            var bicycle2 = new RecordBicycle();
            bicycle2.Pedal();
            var bicycle3 = new StructBicycle();
            bicycle3.Pedal();
        }
    }

    public class ClassBicycle
    {
        public void Pedal()
        {
            Console.WriteLine("[class]踩着踏板向前骑...");
        }
    }

    public record RecordBicycle
    {
        public void Pedal()
        {
            Console.WriteLine("[record]踩着踏板向前骑...");
        }
    }

    public struct StructBicycle
    {
        public void Pedal()
        {
            Console.WriteLine("[struct]踩着踏板向前骑...");
        }
    }
}

运行结果如下:

[class]踩着踏板向前骑...
[record]踩着踏板向前骑...
[struct]踩着踏板向前骑...

为了节省文章篇幅,本文后续将只用类(class)来举例演示。

类、记录和结构成员可访问性

可以使用六种访问类型中的任意一种声明类和记录成员(包括嵌套的类、记录和结构)。结构成员无法声明为protectedprotected internalprivate protected,因为结构不支持继承。

通常情况下,成员的可访问性不大于包含该成员的类型的可访问性。但是,如果内部类的public成员实现了接口方法或替代了在公共基类中定义的虚拟方法,则可从该程序集的外部访问该成员。

为字段、属性或事件的任何成员的类型必须至少与该成员本身具有相同的可访问性。 同样,任何方法、索引器或委托的返回类型和参数类型必须至少与该成员本身具有相同的可访问性。 例如,除非C也是public,否则不能具有返回类Cpublic方法M。同样,如果A声明为private,则不能具有类型Aprotected属性。

变量作用域

C#语言中变量的作用域是指可以访问该变量的代码区域。

目前我们已经学习了在C#程序的不同位置声明变量,比如:

  • 在类的主体中作为类的字段,这里声明的变量也叫作成员变量,在整个类中都有效。成员变量又可分为实例变量和静态变量
  • 作为方法或者构造函数的参数
  • 在方法体或者构造函数体内部
  • 在语句块内部,比如:if,while,for等语句块内部

那么,接下来我们就详细了解C#语言中不同位置声明变量的作用域。

一般情况下,确定作用域遵循以下规则:

  • 只要类在某个作用域中,其字段也(也称成员变量)也在该作用域内。
  • 局部变量存在于表示声明该变量的块语句或方法结束的右花括号({})之前的作用域内。
  • 在for、while或类似语句中声明的局部变量存在于该循环体内。

生活实例: 拿居民小区来举例,在中国,不同的居民小区是有分界的,每个物业公司管理着不同的小区。我们定义一个类,就好比建立一个居民小区,类中的一个成员变量,就如小区中的一种共有资源(休息室,健身器材等),每个小区的业主正常情况下只能使用本小区的资源),在方法内部或者语句块内部的变量则如每个业主家自己的资源(每个业主可以共同使用该小区的资源,但业主的资源是不能共用的,小区物业也不知道业主家的具体资源,所以小区物业也不能访问业主的资源)。

如果类的成员变量和局部变量同名,则在方法或者语句块中使用成员变量时需要使用this关键字指定。

类的静态私有变量使用示例

以下为一个演示类中静态私有字段作用域的实例代码:

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var plusResult = SimpleCalculator.Plus();
            var minusResult = SimpleCalculator.Minus();
            Console.WriteLine($"x+y={plusResult}");
            Console.WriteLine($"x+y={minusResult}");
        }
    }

    /// <summary>
    /// 简单计算器
    /// </summary>
    public static class SimpleCalculator
    {
        /// <summary>
        /// 位于SimpleCalculator类中的静态字段:x
        /// 作用域为SimpleCalculator类
        /// </summary>
        private static int x = 3;
        /// <summary>
        /// 位于SimpleCalculator类中的静态字段:y
        /// 作用域为Program类
        /// </summary>
        private static int y = 2;

        /// <summary>
        /// 相加
        /// </summary>
        public static int Plus()
        {
            return x + y;
        }

        /// <summary>
        /// 相减
        /// </summary>
        public static int Minus()
        {
            return x - y;
        }
    }
}

运行结果如下:

x+y=5
x+y=1

我们只能调用SimpleCalculator.Plus()或者SimpleCalculator.Minus()方法。

类的方法参数作用域示例

以下为C#类的方法参数作用域示例代码:

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var plusResult = SimpleCalculator.Plus(30, 20);
            Console.WriteLine($"x+y={plusResult}");
        }
    }

    /// <summary>
    /// 简单计算器
    /// </summary>
    public static class SimpleCalculator
    {
        /// <summary>
        /// 相加
        /// </summary>
        /// <param name="x">参数x</param>
        /// <param name="y">参数y</param>
        /// <returns></returns>
        public static int Plus(int x, int y)
        {
            // 参数x,y的作用域只能在Plus这个方法块内部
            return x + y;
        }
    }
}

这里,SimpleCalculator类是的Plus()方法接收两个int类型的参数,其方法参数x,y的作用域只能在Plus这个方法块内部,比如在以下的Minus()方法内部访问Plus()方法参数是错误的:

编译器报错:无法解析符号“x”

访问修饰符

与作用域类似,在C#程序开发中,所有的类型和类型成员都具有可访问级别。可访问级别控制着这个其他类型,类型成员或者程序集中对这个类型和类型成员的可使用性。可以使用以下的访问修饰符来声明类型或者类型成员的可访问性:

  • public :访问不受限制(相同或者不同程序集均可访问此级别的类型和成员)。
  • protected :访问仅限于包含类或从包含类派生的类型。
  • internal :访问仅限于当前程序集。
  • private :访问仅限于包含类。
  • protected internal :访问仅限于从包含类派生的当前程序集或类型。
  • private protected(C# 7.2) :类型或成员只能在其声明的程序集中通过同一类中的代码或派生自该类的类型访问。

访问修饰符的特性

1、一个类型或者类型成员只能同时使用一个访问修饰符(除了组合修饰符protected internalprivate protected外)

2、访问修饰符不允许在命名空间(namespace)上使用。命名空间(namespace)不需要访问修饰符修饰。

3、根据成员声明发生的上下文,只允许某些声明的可访问性。如果成员声明中没有指定访问修饰符,则使用默认的可访问性。

4、如果是顶级类型,即:没有被嵌套在其他类型内,则只可使用internal或者public这两个访问修饰符。如果不写访问修饰符,则它们的默认访问修饰符为internal

5、如果是嵌套类型,即被嵌套类型是其他类型的成员,可以声明可访问性,如下表所示:

上级类型 默认成员的可访问性 允许成员声明的可访问性
enum(枚举) public
class(类) private public , protected, internal, private, protected internal, private protected
interface(接口) public
struct(结构) private public, internal, private

6、类成员和结构成员(包括嵌套类和结构)的访问级别默认为private。被private修饰的嵌套类型不能从包含类型的外部访问。

7、派生类不能比它们的基类型具有更大的可访问性。比如有基类A,其访问级别为protect,类B派生于A,这时修饰类B的访问修饰符不能为public

public访问级别

PublicResidential.cs

/// <summary>
/// 小区
/// </summary>
public class Residential
{
    /// <summary>
    /// 名称
    /// </summary>
    public string Name { get; set; }
    /// <summary>
    /// 坐落
    /// </summary>
    public string Located { get; set; }
    /// <summary>
    /// 居民数
    /// </summary>
    public int NumberOfResidents { get; set; }
    /// <summary>
    /// 楼栋数
    /// </summary>
    public int NumberOfBuildings { get; set; }
    /// <summary>
    /// 管理员
    /// </summary>
    public string Administrator { get; set; }
    /// <summary>
    /// 已有的健身设备
    /// </summary>
    public FitnessEquipment ResidentialFitnessEquipment { get; set; }
    /// <summary>
    /// 健身设备
    /// </summary>
    public class FitnessEquipment
    {
        public FitnessEquipment(string equipmentName)
        {
            EquipmentName = equipmentName;
            Console.WriteLine("安装健身设备{0}...", equipmentName);
        }
        public string EquipmentName { get;private set; }

        public void Used()
        {
            Console.WriteLine("使用健身设备{0}锻炼...",EquipmentName);
        }
    }
}

Resident.cs

/// <summary>
/// 居民
/// </summary>
public class Resident
{
    public Resident(string name, string roomNumber, string telephone = "")
    {
        Name = name;
        RoomNumber = roomNumber;
        Telephone = telephone;
    }
    /// <summary>
    /// 姓名
    /// </summary>
    public string Name { get; set; }
    /// <summary>
    /// 房号
    /// </summary>
    public string RoomNumber { get; set; }
    /// <summary>
    /// 电话
    /// </summary>
    public string Telephone { get; set; }

    public Residential ResidentialArea { get; set; }

    public void DoExercise()
    {
        var fitnessEquipment = new FitnessEquipment("太空漫步机");
        fitnessEquipment.Used();
    }
}

版权声明:本作品系原创,版权归码友网所有,如未经许可,禁止任何形式转载,违者必究。

发表评论

登录用户才能发表评论, 请 登 录 或者 注册