演练:使用 C 持久保存对象#
可以使用二进制序列化在实例之间保留对象的数据,这样就可以存储值并在下次实例化对象时检索这些值。
本演练将创建一个基础的 Loan
对象,并将其值保留在文件中。 当重新创建该对象时,将检索文件中的数据。
重要
如果此文件尚不存在,本示例将新建文件。 如果应用程序必须创建文件,则该应用程序必须对文件夹具有 Create
权限。 可使用访问控制列表设置权限。 如果文件已存在,则该应用程序只需要 Write
权限(这是较弱的权限)。 如有可能,较安全的做法是在部署过程中创建文件并仅向单个文件授予 Read
权限(而不是授予文件夹的“创建”权限)。 此外,较安全的做法是将数据写入用户文件夹,而不是根文件夹或 Program Files 文件夹。
重要
此示例将数据存储为二进制格式的文件。 不应将这些格式用于敏感数据,如密码或信用卡信息。
警告
二进制序列化可能会十分危险。 有关详细信息,请参阅 BinaryFormatter 安全指南。
先决条件
若要生成并运行,请安装 .NET SDK。
安装常用的代码编辑器(如果尚未安装)。
提示
需要安装代码编辑器? 试用 Visual Studio!
可在 .NET 示例 GitHub 存储库在线检查示例代码。
创建贷款对象
第一步是创建 Loan
类和使用该类的控制台应用程序:
- 创建一个新的应用程序。 键入
dotnet new console -o serialization
,在名为serialization
的子目录下创建新的控制台应用程序。 - 在编辑器中打开应用程序,然后添加名为
Loan.cs
的新类。 - 将以下代码添加到
Loan
类:
public class Loan : INotifyPropertyChanged
{
public double LoanAmount { get; set; }
public double InterestRatePercent { get; set; }
[field:NonSerialized()]
public DateTime TimeLastLoaded { get; set; }
public int Term { get; set; }
private string customer;
public string Customer
{
get { return customer; }
set
{
customer = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(Customer)));
}
}
[field: NonSerialized()]
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
public Loan(double loanAmount,
double interestRate,
int term,
string customer)
{
this.LoanAmount = loanAmount;
this.InterestRatePercent = interestRate;
this.Term = term;
this.customer = customer;
}
}
还必须创建使用 Loan
类的应用程序。
串行化 Loan 对象
- 打开
Program.cs
。 添加以下代码:
Loan TestLoan = new Loan(10000.0, 7.5, 36, "Neil Black");
添加 PropertyChanged
事件的事件处理程序和几行以修改 Loan
对象并显示此更改。 你可以在下列代码中查看添加项:
TestLoan.PropertyChanged += (_, __) => Console.WriteLine($"New customer value: {TestLoan.Customer}");
TestLoan.Customer = "Henry Clay";
Console.WriteLine(TestLoan.InterestRatePercent);
TestLoan.InterestRatePercent = 7.1;
Console.WriteLine(TestLoan.InterestRatePercent);
现在,可运行该代码,并查看当前输出:
New customer value: Henry Clay
7.5
7.1
重复运行此应用程序始终编写相同值。 每次运行程序时都会创建一个新 Loan
对象。 在现实生活中,利率会定期更改,但不必在每次运行应用程序时都更改利率。 序列化代码表示在应用程序的实例之间保存最新的利率。 在下一步中,你将通过向 类添加序列化来 Loan
执行此操作。
使用序列化保持对象
若要使用二进制序列化保留 Loan 类的值,必须先使用 Serializable
特性标记类。 在类定义上方 Loan
添加以下代码:
[Serializable()]
告知 SerializableAttribute 编译器,类中的所有内容都可以使用二进制序列化保存到文件中。 因为 PropertyChanged
事件不表示应该存储的对象图的部分,所以它不应序列化。 执行此操作可能将所有附加到该事件的对象序列化。 可将 NonSerializedAttribute 添加到 PropertyChanged
事件处理程序的字段声明。
[field: NonSerialized()]
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
可使用 field
目标值将特性附加到自动实现的属性的支持字段。 以下代码添加 TimeLastLoaded
属性并将其标记为不可序列化:
[field:NonSerialized()]
public DateTime TimeLastLoaded { get; set; }
下一步是向 LoanApp 应用程序添加序列化代码。 为了将该类序列化并将其写入到文件,可使用 System.IO 和 System.Runtime.Serialization.Formatters.Binary 命名空间。 为了避免键入完全限定的名称,可以添加对必要命名空间的引用,如以下代码所示:
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
下一步是添加代码,在创建对象时对文件中的对象进行反序列化。 向序列化数据的文件名的类中添加一个常量,如以下代码所示:
const string FileName = @"../../../SavedLoan.bin";
接下来,在创建 TestLoan
对象的行后添加以下代码:
if (File.Exists(FileName))
{
Console.WriteLine("Reading saved file");
Stream openFileStream = File.OpenRead(FileName);
BinaryFormatter deserializer = new BinaryFormatter();
TestLoan = (Loan)deserializer.Deserialize(openFileStream);
TestLoan.TimeLastLoaded = DateTime.Now;
openFileStream.Close();
}
首先必须检查该文件是否存在。 如果存在,则创建 Stream 类来读取二进制文件和 BinaryFormatter 类,以转换该文件。 还需将流类型转换为 Loan 对象类型。
然后必须添加代码以将该类序列化到文件中。 以 Main
方式在现有代码后添加以下代码:
Stream SaveFileStream = File.Create(FileName);
BinaryFormatter serializer = new BinaryFormatter();
serializer.Serialize(SaveFileStream, TestLoan);
SaveFileStream.Close();
此时可再次生成并运行应用程序。 首次运行时,请注意起始利率为 7.5,然后更改为 7.1. 关闭该应用程序,然后重新运行。 现在,应用程序打印已读取所保存文件的消息,即使在代码更改它之前,利率也是 7.1。