如下面所示,先使用File.OpenWrite方法创建一个用于写入数据到文件的流,然后再创建 StreamWriter对象,并把一个字符串写入文件,最后,调用 ReadAllText方法读取刚才写入的文件内容。有问题的代码
usingvaroutStream-Pile.OpenWrite("e:/1,txt");
usingvarwriter=newStreamMciter (outStream);
writer.WriteLine("hello");
stringn-FLle.ReadA11Text("e:/1,txt");
Console.WriteLine(s);
执行这段代码后,会因为第4行代码抛出如下的异常: System.IO.IOException:"The process cannot access the file 'e:\l.txt' because it is being used byanother process.” 由于outStream和writer两个变量在方法执行结束后才被释放资源,程序在执行到第4行代码的时候,文件仍然被占用,因此第4行代码抛出了异常。如果希望上面的代码能够正常执行,要么使用传统的using语法进行资源的释放,要么手动添加花括号把需要释放的资源放到单独的作用域中,如代码2-8所示。正确的代码
{
using var outStream = File.Openvrite("e:/1.txt");
using var writer= new StreamWriter(outstream);
writer.WriteLine("hello");
}
string s = File.ReadAl1Text("e:/1.txt");
Console.WriteLine(s);
可以看到,在第1~5行代码中,通过手动添加一个花括号构建了一个独立的代码块。根据C#语法的规范,第1~5行代码组成的代码块就是一个独立的作用域,因此程序在离开这个作用域以后,outStream、writer 两个变量指向的对象就会被释放。
文件范围的命名空间声明
在之前版本的C#中,类型必须定义在命名空间中,而从C#10.0开始,C#允许编写独立namespace代码行声明命名空间,文件中所有的类型都是这个命名空间下的成员。这种语法够减少C#源代码文件的嵌套层次,如代码2-9所示。代码2-9 简化的命名空间声明
namespace TMS.Admin;
class Teacher
{
public int Id {get; set}
public string Name {get; set;}
}
可为空的引用类型 我们知道,在C#中,数据类型分为值类型和引用类型,值类型的变量不可以为空,用类型的变量可以为空。但是,在使用引用类型的时候,如果不注意检查引用类型变量是为空,程序中就有可能出现NullReferenceException异常。 C#8.0中提供了“可为空的引用类型”语法,可以在引用类型后添加“?”修饰符声个类型是可为空的。对于没有添加“?”修饰符的引用类型的变量,当编译器发现存在个变量赋值null的可能性的时候,编译器会给出警告信息。在Visual Studio 2022中,这性是默认启用的,可以通过删除项目*.csproj 文件中的<Nulable>disable</Nullable>关闭特性。 编写一个包含Name、PhoneNumber两个属性的Student类。没有应用可为空的引用类型的类
public class Student
{
public string Name { get; set; }
public string PhoneNumber { get; set; }
public Student(string name)
{
this.Name = name;
}
}
上面的代码在编译的时候,编译器会给出“在退出构造方法时,不可为null的属PhoneNumber必须包含非 null 值”这样的警告信息。Name、PhoneNumber两个属性都是 str类型,因此它们都是“不可为空的string类型”,但是Student类的构造方法中只为Name赋值了,这样就存在PhoncNumber属性没有被赋值,从而导致其属性值为空的可能性,因编译器给出了这样的警告信息。如果想消除这个警告信息,可以将构造方法声明Student(string name, string phoneNumber),并为两个属性都赋值,但是如果PhoneNumber 属确实可以为空,就可以把PhoneNumber属性声明为string?类型,也就是允许为空的string类如代码2-11所示。使用可为空的引用类型的类
public class Student
{
public string Name { get; set;}
public string? PhoneNumber { get; set; }
public Student(string name)
{
this.Name=name;
}
}
由于上面定义的 Student 类的PhoneNumber属性可能为空,因此下边代码中的第3行代码执行后会出现“解引用可能出现空引用”这样的警告信息。没有进行可为空处理的代码
Student s1 = GetData();
Console,WriteLine(sl.Name.ToLower());
Console.WriteLine(a1.PhoneNumber.ToLower());
Student GetData()
{
Student s1 = new Student("Zack");
s1.FhoneNumber="999";
return s1;
}
可以用下边代码所示的方法对PhoneNumber属性进行非空检查来避免这个警告。对可为空类型的成员进行检查
Student s1 = GetData();
Console.WriteLine(s1.Name.ToLower());
if (s1.PhoneNumber!= nu1l)
{
Console.WriteLine(s1.PhoneNumber.ToLower());
}
else
{
Console.WriteLine("手机号为空");
}
当然,如果确认被访问的变量、成员不会出现为空的情况,也可以在访问可为空的变量、成员的时候加上!来抑制编译器的警告,如下边代码所示。当然,要尽量避免使用!抑制警告。使用!抑制警告
Student s1= GetData();
Console.WriteLine(sl.Name,ToLower ());
Console.WriteLine(s1,PhoneNumber!.ToLower());
对于可为空的引用类型的属性,编译器会在属性上添加 NullableAttribute,因此可以在运行时通过反射判断一个引用类型属性的可空性。很多.NET下的框架都充分利用了可为空的引用类型,从而对引用类型的属性、参数等进行更加智能化的处理。