我正在尝试使用c#8.0并希望为整个项目启用空引用类型检查。我希望我可以改进我的代码设计,但不要在某些代码范围内禁用此检查。
我反序列化对象图时遇到了问题。该对象具有相互引用,但对于最终用户视图,整个对象图,所有引用都必须具有值。
因此在反序列化期间,引用可以为null,但是在加载所有对象之后,最终解析过程将链接所有引用。
我尝试了一些写风格,一些解决方案是工作,但扩展了很多代码,似乎不是一个优雅的方式。
例如,我尝试转储代码为每种对象编写一个对类,并在反序列化期间将它们用作临时对象。这些对类的所有引用都可以为null。反序列化完成后,复制所有字段并转换为真实对象。当然,这样我需要编写很多代码。
或者我尝试放置一个可空字段和一个不可空的属性。为这样的字段添加内部setter。这种方式代码较少,但也增加了我的代码库。
传统的,不考虑性能,反序列化只是使用反射,因此几乎没有加载代码。但编写我自己的解析代码有一些好处,例如我可以输出更多有用的错误信息包括一些提示。
但是当我介绍可以为空的字段时,我的解析代码会增加很多。我想知道这是否是一个好的和正确的方法吗?
对于代码示例,我尽可能简化代码。最后的示例代码并不是太多,但实际上我的课程远不止这些。所以我想知道是否有更好的代码风格。
class Person
{
private IReadOnlyList<Person>? friends;
internal Person(string name)
{
this.Name = name;
}
public string Name { get; }
public IReadOnlyList<Person> Friends => this.friends!;
internal SetFriends(IReadOnlyList<Person> friends)
{
this.friends = friends;
}
}
class PersonForSerialize
{
public string? Name { get; set; }
public IReadOnlyList<string> Friends { get; set; }
}
IReadOnlyList<Person> LoadPeople(string path)
{
PersonForSerialize[] peopleTemp = LoadFromFile(path);
Person[] people = new Person[peopleTemp.Count];
for (int i = 0; i < peopleTemp.Count; ++i)
{
people[i] = new Person(peopleTemp[i].Name);
}
for (int i = 0; i < peopleTemp.Count; ++i)
{
Person[] friends = new Person[peopleTemp[i].Friends.Count];
for (int j = 0; j < friends.Count; ++j)
{
string friendName = peopleTemp[i].Friends[j];
friends[j] = FindPerson(people, friendName);
}
people[i].SetFriends(friends);
}
}
虽然由于C#8中的不可为空的默认值而得到强调,但这确实是一个常见的“循环依赖”问题,它始终存在于相互依赖的构造中。
如您所见,一种标准解决方案是使用受限制的setter。使用C#8,您可能希望在对象图的反序列化期间使用“null-forgiving”null!
- 允许您将未构造的集与有效的空集区分开,并最小化分配。
例:
class Person
{
internal Person(string name, IReadOnlyList<Person> friends)
{
Name = name; Friends = friends
}
public string Name { get; }
public IReadOnlyList<Person> Friends {get; internal set;}
}
class SerializedPerson { ... }
IEnumerable<Person> LoadPeople(string path)
{
var serializedPeople = LoadFromFile(path);
// Note the use of null!
var people = serializedPeople.Select(p => new Person(p.Name, null!));
foreach(var person in people)
{
person.Friends = GetFriends(person, people, serializedPeople);
}
return people;
}