可以为空的属性,或允许为空的属性,代码异味(在 OOP 语言中)吗?

我经常看到classes喜欢


public class Person

{

    string FirstName { get; set; }


    string MiddleName { get; set; }


    string LastName { get; set; }


    int Age { get; set; }

}

其中一个属性,比如MiddleName在这个例子中(因为有些人在出生时没有中间名),是允许的null。在我看来这是错误的,因为该类的任何消费者都必须知道该属性“可能”为空并执行类似的检查


public void PrintName(Person p)

{

    Console.WriteLine(p.FirstName);


    if (p.MiddleName != null)

    {

        Console.WriteLine(p.MiddleName);

    }


    Console.WriteLine(p.LastName);

}

我如何处理这个是通过使用像


public interface IPerson

{

    string FirstName { get; }


    string LastName { get; }


    int Age { get; }

}


public interface IMiddleNamed

{

    string MiddleName { get; }

}


public void PrintName(IPerson p)

{

    Console.WriteLine(p.FirstName);


    if (p is IMiddleNamed)

    {

        Console.WriteLine((p as IMiddleNamed).MiddleName);

    }


    Console.WriteLine(p.LastName);

}

即使我的模型代表数据合同(例如,通过服务间通信序列化/反序列化的“价值袋”)也是如此。


以同样的方式,我避免使用具有null-able 值类型的类,例如


public class Something

{

    public int SomeInt { get; set; }


    public double? SomeDouble { get; }

}

出于同样的原因。


但是我想知道这是否像“OOP 过度工程”,因为它显然增加了很多开发时间。


四季花海
浏览 135回答 2
2回答

三国纷争

我看到当前的答案是关于字符串的(例如 c# 中的字符串类处理它),但我想你的问题涵盖了所有可能的可为空对象。1)首先,关于?? 操作员在另一个答案中建议:它将允许您的代码与空对象一起使用,但在某些时候,您通常仍然需要处理空情况例子:if(a != null && a.b != null and a.b.c != null)&nbsp;{&nbsp; &nbsp; a.b.c.doSomething();}会变成if(a?.b?.c? != null){&nbsp; &nbsp; a.b.c.doSomething();}是的,它好一点,但并不能真正解决您的问题。2)代码架构命题我发现通常,allownull意味着你的类有多种用途,并带有更多的状态。在此示例中,您的类可以表示“具有中间名的名称”或“没有全名的名称”。这里有一些关于代码架构的想法通常,这意味着这个类做了太多的事情。当然,在您的示例中,班级很小。但在日常生活中,有一个可以为空的成员通常意味着类太大,支持的特性太多。一个班级应该只有一个目的。参见单一职责原则在某些情况下(如示例中),您可能会认为您不想拆分课程,因为它是有意义的。然后你可以尝试看看是否有办法认为这个类具有相同的功能,而无需处理特殊情况。在您的示例中,您可以执行以下操作:public class Person{&nbsp; &nbsp; string[] FirstNames { get; set {if(value == null) FirstName } = new string[]{};&nbsp; &nbsp; string LastName { get; set; } = string.Empty;&nbsp; &nbsp; int Age { get; set; }}现在没有特殊情况了。所有函数都必须考虑一个名称数组,其大小可能为 1。如果您真的非常想要一个空对象,您还可以将处理它的逻辑放在类中,并确保没有其他人访问该空对象。但这可能会导致在同一个班级中有大量的逻辑,所以这不是最好的解决方案在你的例子中你可以做public class Person{&nbsp; &nbsp; private string FirstName { get; set {if(value == null) FirstName } = string.Empty;&nbsp; &nbsp; private string MiddleName { get; set; } = string.Empty;&nbsp; &nbsp; string LastName { get; set; } = string.Empty;&nbsp; &nbsp; int Age { get; set; }&nbsp; &nbsp; public string getName()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; // Here handle the null case&nbsp; &nbsp; }}你可以看到这如何在课堂上增加了逻辑性。最后,您可以阅读有关Null 对象模式的信息。此模式需要更多代码,但允许您替换null为对象的实际实例,当您在它们上调用函数时不会崩溃。我从来没有积极地使用过它,因为我通常设法通过上述原则获得更清晰的代码,但你可能会发现这很有用的一个案例。下面是来自维基百科的 Null Object 实现示例/* Null object pattern implementation:&nbsp;*/using System;// Animal interface is the key to compatibility for Animal implementations below.interface IAnimal{&nbsp; &nbsp; void MakeSound();}// Animal is the base case.abstract class Animal : IAnimal{&nbsp; &nbsp; // A shared instance that can be used for comparisons&nbsp; &nbsp; public static readonly IAnimal Null = new NullAnimal();&nbsp; &nbsp; // The Null Case: this NullAnimal class should be used in place of C# null keyword.&nbsp; &nbsp; private class NullAnimal : Animal&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; public override void MakeSound()&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Purposefully provides no behaviour.&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; public abstract void MakeSound();}// Dog is a real animal.class Dog : IAnimal{&nbsp; &nbsp; public void MakeSound()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; Console.WriteLine("Woof!");&nbsp; &nbsp; }}/* =========================&nbsp;* Simplistic usage example in a Main entry point.&nbsp;*/static class Program{&nbsp; &nbsp; static void Main()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; IAnimal dog = new Dog();&nbsp; &nbsp; &nbsp; &nbsp; dog.MakeSound(); // outputs "Woof!"&nbsp; &nbsp; &nbsp; &nbsp; /* Instead of using C# null, use the Animal.Null instance.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* This example is simplistic but conveys the idea that if the&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Animal.Null instance is used then the program&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* will never experience a .NET System.NullReferenceException at runtime, unlike if C# null were used.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; &nbsp; &nbsp; IAnimal unknown = Animal.Null;&nbsp; //<< replaces: IAnimal unknown = null;&nbsp; &nbsp; unknown.MakeSound(); // outputs nothing, but does not throw a runtime exception&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; }}

www说

有时空值肯定是有用的,例如当您有一个代表一组问题答案的对象时,例如 bool? TrueFalseAnswer == null 表示未回答问题,而不是默认为 false,或者 string name == null 表示未填写名称字段等。就您的类而言,您至少可以将它们初始化为空字符串。public class Person{&nbsp; &nbsp; string FirstName { get; set {if(value == null) FirstName } = string.Empty;&nbsp; &nbsp; string MiddleName { get; set; } = string.Empty;&nbsp; &nbsp; string LastName { get; set; } = string.Empty;&nbsp; &nbsp; int Age { get; set; }}另外,我不认为这就是你的意思,但是:string s = null;Console.WriteLine(s);将新行输出到控制台并且不会导致错误。再说一次,它也很容易输入:Person p = new Person();//if(p == null || p.MiddleName == null) s = "";string s = p?.MiddleName ?? "";因此,空值很有用,C# 让检查它们变得超级容易。
打开App,查看更多内容
随时随地看视频慕课网APP