1. 变量的作用域有哪些?
变量的作用域决定了变量的可见性和生命周期。下面分别解释局部(Local)变量、块级(Block-level)参数,与局部变量、方法参数(Method Parameters)、全局(Global,在C#中更准确地说是类级别的成员变量)变量、静态(Static)变量的作用域:
1.1 局部变量(Local Variables)
- 作用域:局部变量在它们被声明的代码块(如方法体、if语句块、for循环体等)内部是可见的。一旦离开这个代码块,该变量就不再可访问。
- 示例代码:
-
void xx()
{
int a = 10; //局部变量,方法体内,作用域
a = 20;
}
for(int i=0; i<10; i++)
{
i++; //局部变量,for循环体内有效
}
1.2 块级变量(Block-level Variables)
在C#中,并没有特别称为“块级变量”的术语。局部变量的概念已经涵盖了所有在代码块中声明的变量,无论这个代码块是方法体、循环体、条件语句体等。作用域和生命周期与局部变量相同。
示例代码:
{
int x = 0; //块的变量,作用域,块内有效
x = 10;
}
1.3 方法参数(Method Parameters)
- 作用域:方法参数在方法体内是可见的,并且可以在方法体内被访问和修改(除非它们是
readonly
的)。
1.4 全局变量(在C#中的术语是类级别的成员变量)
- 作用域:在C#中,没有真正的全局变量(即在整个程序中都可访问的变量)。如果它们没有被声明为
private
(私有)。如果它们是public
、protected
或internal
的,它在整个对象的所有的方法中都可以调用。
1.5 静态变量(Static Variables)
- 作用域:静态变量在声明它们的类的所有实例之间共享。这意味着,无论创建了多少个类的实例,都只有一个静态变量的副本。静态变量可以通过类名直接访问(如果它们是
public
或internal
的),或者通过类的实例访问(无论访问修饰符是什么)。
2. 成员变量和静态变量的区别?
2.1 所属范围
- 成员变量:属于类的实例(对象)。每个对象都有其自己的成员变量副本,这些变量在对象创建时初始化,并在对象被销毁时释放。
- 静态变量:属于类本身,而不是类的任何特定实例。静态变量在内存中只有一个副本,被类的所有实例共享。
2.2 访问方式
- 成员变量:只能通过类的实例(对象)来访问。需要使用对象名加上点操作符(.)来访问成员变量。
- 静态变量:可以通过类的实例来访问,但更常见的是通过类名直接访问,因为静态变量与类的实例无关。使用类名加上点操作符(.)来访问静态变量。
2.3 生命周期
- 成员变量:成员变量的生命周期与它们所属的对象的生命周期相同。当对象被创建时,成员变量被初始化(如果未显式初始化,则赋予默认值)。当对象被销毁时,成员变量也随之释放。
- 静态变量:静态变量的生命周期从程序开始执行时开始,一直持续到程序结束。静态变量在类的所有实例之间共享,因此它们的值在程序的不同部分中被保留和修改。
2.4 存储位置
- 成员变量:存储在堆内存中,因为它们是对象的属性,而对象是在堆上创建的。
- 静态变量:存储在方法区的静态区(也称为元数据区),因为它们与类相关联,而不是与特定的对象实例相关联。
2.5 默认值
- 成员变量:对于数值类型的成员变量,默认值是0;对于布尔类型的成员变量,默认值是false;对于引用类型的成员变量,默认值是null。
- 静态变量:同样遵循上述默认值规则,但它们的值在程序运行期间是共享的,因此任何对静态变量的修改都会影响到所有实例。
2.6 初始化
- 成员变量:可以在声明时直接初始化,也可以在构造函数中初始化。
- 静态变量:可以在声明时直接初始化,或者在静态构造函数中初始化。静态构造函数在类被加载到内存中时自动执行一次。
2.7 用途
- 成员变量:用于存储与对象实例相关的数据。
- 静态变量:通常用于存储与整个类相关的数据,如计数器、全局配置等。静态变量可以在没有创建类的实例的情况下访问,因此它们可以用于在类的所有实例之间共享数据。
3. 利用递归,写个文件目录遍历,打印出文件名、扩展名、文件大小
3.1: 创建 CSHomeworkWork2.cs
的新类文件。
代码如下
using System; using System.IO; namespace DJConsoleProject { public class CSHomeworkWork2 { public void TraverseDirectory(string directoryPath) { // 确保路径存在 if (!Directory.Exists(directoryPath)) { Console.WriteLine("Directory does not exist."); return; } // 获取所有文件和子目录 string[] files = Directory.GetFiles(directoryPath); string[] dirs = Directory.GetDirectories(directoryPath); // 遍历文件 foreach (string file in files) { FileInfo fileInfo = new FileInfo(file); Console.WriteLine($"File Name: {fileInfo.Name}, Extension: {fileInfo.Extension}, Size: {fileInfo.Length} bytes"); } // 递归遍历子目录 foreach (string dir in dirs) { TraverseDirectory(dir); } } } }
3.2 编写 Program 类
在 Program.cs
文件中,确保 Main
方法调用 CSHomeworkWork2
类的 TraverseDirectory
方法。
using System;
using DJConsoleProject;
namespace DJConsoleProject
{
class Program
{
static void Main(string[] args)
{
CSHomeworkWork2 homework = new CSHomeworkWork2();
string startDirectory = @"D:\VS";
homework.TraverseDirectory(startDirectory);
}
}
}
3.3 编译和运行
4. 简述访问修饰符有几种,各有什么不同?
在C#中,访问修饰符用于设置类型(如类、接口、结构体等)和类型成员(如字段、属性、方法等)的可访问性级别。
4.1 public
- 定义:公有访问修饰符,表示无限制访问。
- 作用域:任何地方都可以访问被声明为
public
的成员或类型,无论访问点是否在同一程序集、类或其他类型中。 - 用途:常用于需要被外部访问的类、接口成员或类型。
4.2 private
- 定义:私有访问修饰符,表示访问仅限于声明它的类型内部。
- 作用域:只有同一类型(类、结构体等)中的成员可以访问被声明为
private
的成员。 - 用途:常用于隐藏类的内部实现细节,保护数据不被外部直接访问。
4.3 protected
- 定义:受保护访问修饰符,表示访问限于同一类型及其派生类型。
- 作用域:只能在声明它的类型及其所有派生类中被访问。注意,即使派生类位于不同的程序集中,也可以访问受保护的成员。
- 用途:常用于基类中的成员,这些成员需要被派生类访问,但不应被其他类型访问。
4.4 internal
- 定义:内部访问修饰符,表示访问限于同一程序集内。
- 作用域:在同一程序集中的任何类型都可以访问被声明为
internal
的成员或类型。但是,如果访问点位于不同的程序集中,则无法访问。 - 用途:常用于程序集内部共享的类型或成员,但不希望它们被外部程序集访问。
4.5 protected internal
- 定义:内部受保护访问修饰符,表示访问限于当前程序集或派生自包含类的类型。
- 作用域:这是
protected
和internal
的组合,允许同一程序集中的任何类型或任何从包含类派生的类型访问被声明为protected internal
的成员。 - 用途:当需要在程序集内部提供访问权限,并且还需要允许派生类(无论它们是否在同一程序集中)访问时,这个访问修饰符非常有用。
总结
public
:无限制访问。private
:仅限声明它的类型内部访问。protected
:仅限同一类型及其派生类型访问。internal
:仅限同一程序集内访问。protected internal
:当前程序集或派生自包含类的类型可以访问。
拓展:public和protected比较,代码示例
- BaseClass 基类:两个属性,两个方法,分别使用这两个修饰符
- DerivedClass 派生类(子类)
- Program
BaseClass.cs 基类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DJConsoleApp527
{
internal class BaseClass
{
public int pc;
protected int pd;
public void pcm()
{
Console.WriteLine("public method");
}
protected void pdm()
{
Console.WriteLine("protected method");
}
}
}
DerivedClass.cs 派生类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DJConsoleApp527
{
internal class DerivedClass : BaseClass
{
public void accessMember()
{
pc = 10; //直接访问父类的public属性
pd = 20; //直接访问父类的protected属性
pcm(); //直接访问父类的public方法
pdm(); //直接访问父类的protected方法
}
}
}
调用 Program.cs
using DJConsoleApp527;
DerivedClass dc = new DerivedClass();
dc.accessMember(); //外部类通过派生类去访问基类
dc.pc = 10; //public属性在哪都可以
//dc.pd = 20; //报错:外部类属性不能直接访问,只能子类去访问
dc.pcm(); //public方法在哪都可以
//dc.pdm(); //报错:外部类方法不能直接访问,只能子类去访问
5. 重点比较public、protected、private的区别
5.1 public
- 定义:公有访问修饰符,表示无限制访问。
- 作用域:
- 对其访问没有限制,任何地方都可以访问被声明为
public
的成员或类型。 - 包括同一程序集内的其他类型、不同程序集中的类型,以及任何派生自该类型的子类。
- 对其访问没有限制,任何地方都可以访问被声明为
- 用途:
- 常用于需要被外部访问的类、接口成员或类型。
- 例如,公开API接口中的方法或属性,以便外部代码可以调用或访问。
5.2 protected
- 定义:受保护访问修饰符,表示访问限于同一类型及其派生类型。
- 作用域:
- 只能在声明它的类型及其所有派生类中被访问。
- 即使派生类位于不同的程序集中,也可以访问受保护的成员。
- 用途:
- 常用于基类中的成员,这些成员需要被派生类访问,但不应被其他类型访问。
- 保护基类中的实现细节,同时允许派生类进行扩展或修改。
5.3 private
- 定义:私有访问修饰符,表示访问仅限于声明它的类型内部。
- 作用域:
- 只有同一类型(类、结构体等)中的成员可以访问被声明为
private
的成员。 - 外部类型,包括派生类,都无法直接访问私有成员。
- 只有同一类型(类、结构体等)中的成员可以访问被声明为
- 用途:
- 常用于隐藏类的内部实现细节,保护数据不被外部直接访问。
- 确保类的封装性和数据的安全性。
归纳与比较
- 访问范围:从宽到窄依次是
public
(无限制)>protected
(类型及其派生类)>private
(类型内部)。 - 用途差异:
public
用于公开API或需要被外部广泛访问的成员。protected
用于基类与派生类之间的成员共享,保护基类实现的同时允许派生类扩展。private
用于隐藏类的内部实现,确保数据安全和类的封装性。
- 安全性与封装性:随着访问范围的缩小,安全性和封装性逐渐增强。
private
提供了最高的封装性和最低的外部访问性,而public
则相反。