消除冗余空检查:c#中的方法分解和可空性

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

关于可空性的一切在 C# 中都很棒。但是我们在分解代码的时候应该怎么处理呢?

想象一下,我们有一个大方法。它接收一些对象,检查重要字段是否为空,然后处理它们。在方法结束之前,编译器认为属性是非空的:

        private class Person
        {
            public string? Name { get; set; }
        }
        
        //before
        private void Do(Person person)
        {
            if (person.Name is null)
                return;
            Console.WriteLine(person.Name.Contains("John"));
            Console.WriteLine(person.Name.Length);
        }

这个方法很大,所以我们必须把它分解成一组小的方法:

      
        //after
        private void Do(Person person)
        {
            WriteIfNameIsJohn(person);
            WriteNameLength(person);
        }

        private void WriteNameLength(Person person)
        {
            if (person.Name != null)
                Console.WriteLine(person.Name.Length);
        }

        
        private void WriteIfNameIsJohn(Person person)
        {
            if (person.Name != null)
                Console.WriteLine(person.Name.Contains("John"));
        }

但是现在我们应该在每个新方法的开头检查属性是否为空!如果我们创建了三个方法——检查三次!对于每个“提取方法”操作,都需要添加空检查。

可能但冗长且不干净的解决方案是使用某种“已验证”类型并将其传递给新方法:

        private class VerifiedPerson : Person
        {
            public override string Name { get; set; }
            
            private VerifiedPerson(string name)
            {
                Name = name;
            }

            public static VerifiedPerson? GetVerifiedOrNull(Person person) =>
                person.Name is { } name
                    ? new VerifiedPerson(name)
                    : null;
        }

如何以优雅的方式消除这些检查?也许有可能以某种方式传递对象属性的空状态?

谢谢)

c# refactoring nullable decomposition
2个回答
0
投票

一个简单的解决方案是检查这些方法中的空值。这将验证留给了调用者,但由于所有方法都是私有的,所以应该是可以接受的。现在该对象仍在验证中,但只在一个地方。不过,您可以确保在没有首先验证参数的情况下不要调用这些辅助方法:

private void Do(Person person)
{
    if (person?.Name != null)
    {
        WriteIfNameIsJohn(person);
        WriteNameLength(person);
    }
}

private void WriteNameLength(Person person)
{
    Console.WriteLine(person.Name.Length);
}

private void WriteIfNameIsJohn(Person person)
{
    Console.WriteLine(person.Name.Contains("John"));
}

如果验证检查很麻烦,可以将它们拉出到辅助方法中:

private bool IsValid(Person person)
{
    if (person == null) return false;
    if (person.Name == null) return false;
    // etc...
    return true;
}

private void Do(Person person)
{
    if (IsValid(person))
    {
        WriteIfNameIsJohn(person);
        WriteNameLength(person);
    }
}

0
投票

我同意 Rufus,没有人强迫你检查 null,特别是当所有这些方法都是私有的并且因此在你的控制之下时。在任何这些私有方法中,成员都不可能为空。

我们可以帮助编译器使用null-forgiving-operator

!
,例如:

private void WriteNameLength(Person person)
{
    Console.WriteLine(person.Name!.Length);
}

但是我认为当你的逻辑如此庞大时,可能值得考虑从方法中提取一个新类并将名称作为该类的成员。然后,您可以在类的构造函数中轻松执行一次检查,而忘记所有私有方法中的任何空值。

//after
private void Do(Person person)
{
    if(person.Name is null) return;

    var writer = new NameWriter(person.Name);
    writer.WriteIfNameIsJohn();
    writer.WriteNameLength();
}

class NameWriter
{
    private readonly string name; // name is not nullable here
    public MyClass(string name) { this.name = name }

    public void WriteNameLength()
    {
        // no need for any null-checks here
        Console.WriteLine(this.name.Length);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.