在反序列化期间使用c#8.0空引用类型时的最佳实践

问题描述 投票:0回答:1

我正在尝试使用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# .net-core visual-studio-2019 c#-8.0
1个回答
0
投票

虽然由于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;
}
© www.soinside.com 2019 - 2024. All rights reserved.