前言
最近为了搞Windows桌面窗口编程学了C#,让我对“类”这个概念有了更全面的了解。 以前了解“类”这个概念都是通过JS、Python这些解释语言,因为解释型语言本身的限制,它们对“类”的机制实现是不完整的。 导致我对类的访问权限、静态/动态成员等概念总是一知半解。
C#是我目前所知对类实现的最完善的语言了,今天就以C#语言为例,给大家分享一些我对类的见解。
静态/动态成员
类成员在声明时带有static
修饰符的是静态成员,不带static
修饰符的是动态成员。
静态成员只能通过类名来访问,动态成员只能通过实例名来访问。
例如:
using System;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
ClassA insA = new ClassA(); // 实例化类ClassA;创建实例insA。
Console.WriteLine(insA.i); // 通过实例名访问动态成员变量。
// Console.WriteLine(ClassA.i); // 通过类名访问动态成员变量,会报错。
Console.WriteLine(ClassA.iStatic); // 通过类名访问静态成员变量。
// Console.WriteLine(insA.iStatic); // 通过实例名访问静态成员变量,会报错。
Console.WriteLine("/n/n");
insA.Show(); // 通过实例名调用动态成员函数。
Console.WriteLine("/n/n");
ClassA.StaticShow(); // 通过类名调用静态成员函数。
}
}
// 类ClassA
class ClassA
{
public int i = 11; // 动态成员变量i。
public static int iStatic = 99; // 静态成员变量iStatic。
public void Show() // 动态成员函数Show
{
Console.WriteLine(this.i); // 在动态成员函数中访问动态成员变量要加上'this.'前缀。
// 因为此时还未实例化类,所以用'this'来代指类的实例名;
// 在通过实例名'insA'调用动态成员函数时,函数内的'this'语句会被自动替换为实例名'insA';
// 实际上还是通过实例名访问动态成员变量。
Console.WriteLine(iStatic); // 在成员函数中访问静态变量不用加任何前缀。
// 同样,因为此时还未实例化类,所以不加前缀也等同于通过类名访问静态成员变量。
}
public static void StaticShow() // 静态成员函数StaticShow
{
// Console.WriteLine(this.i); // 静态成员函数不能通过实例名调用,也就没法把'this'语句替换为实例名。
// 所以在静态成员函数中无法访问动态成员变量。
Console.WriteLine(iStatic); // 在静态成员函数中访问静态变量和在动态成员函数中一样,不用加任何前缀。
}
}
}
注意
-
静态/动态成员概念,限制的是能通过类名还是实例名访问成员。
-
而公开/私有成员等概念,限制的是能在什么位置访问成员。
这完全是两个不同的概念,千万不要混淆!!!
访问权限修饰符
访问权限修饰符决定了修饰对象的访问权限,即在什么位置可以访问该对象。
首先我们来看一下各访问权限的可访问范围关系图:
我们先来讲两个最简单的:
-
public
修饰符:完全公开权限,在任何位置都能访问。 -
private
修饰符:完全私有权限,只有在定义对象的类内部才能访问; 私有对象一定要搭配一个访问它的方法,这样才能在类外部通过调用该方法间接访问私有对象。
using System;
Cls cls = new Cls();
Console.WriteLine(cls.strPub); // 直接外部访问公开字符串。
cls.ShowStrPri(); // 通过公开方法ShowStrPri间接访问私有字符串。
// Console.WriteLine(cls.strPri); // 直接外部访问私有字符串会报错。
class Cls
{
public string strPub = "公开字符串";
private string strPri = "私有字符串";
public void ShowStrPri()
{
Console.WriteLine(strPri);
}
}
剩下的两个访问权限修饰符有点难理解:
-
internal
修饰符:在当前程序集中可访问。 简单来讲,C#中的程序集有点类似于Go语言中的包(package)概念,都是对应着编译过程中的模块(module)。 在编译过程中,一个源模块中的源代码会一起编译,多个源模块编译生成的目标代码(目标模块)再通过链接器被链接成完整的程序。 所以程序集权限实际上就是限制了对象只能在同一个编译模块中被访问。 -
protected
修饰符:在定义对象的类及其子类中可访问。
注意
-
程序集权限和保护权限范围的交集是:在同一个程序集中的定义对象的类及其子类; 而私有权限的范围是:定义对象的类; 二者并不相等!!!
-
程序集权限的访问范围除去与保护权限重叠的部分,剩下的部分为:在同一个程序集中除定义对象的类及其子类之外的部分。
-
保护权限的访问范围除去与程序集权限重叠的部分,剩下的部分为:不在同一个程序集中的子类。
下面是一个不太完整的代码示例,只是演示了保护对象的权限,程序集权限太麻烦就没有演示:
using System;
ClsSon cls = new ClsSon();
Console.WriteLine(cls.strInter); // 直接外部访问程序集字符串。
cls.ShowStrPro(); // 通过子类ClsSon的公开方法ShowStrPro间接访问父类ClsFather的保护字符串。
// Console.WriteLine(cls.strPro); // 直接外部访问保护字符串会报错。
class ClsFather
{
protected string strPro = "保护字符串";
internal string strInter = "程序集字符串";
}
class ClsSon : ClsFather
{
public void ShowStrPro()
{
Console.WriteLine(strPro);
}
}
最后还有一个组合权限:
internal protected
或 protected internal
:保护权限与程序集权限访问范围的并集——同一个程序集以及不同程序集中的子类。
{{m.name}}
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/97985.html