c# - 可以为空的属性,或允许为空的属性,代码异味(在 OOP 语言中)吗?
问题描述
我经常看到class
es喜欢
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 过度工程”,因为它显然增加了很多开发时间。
解决方案
我看到当前的答案是关于字符串的(例如 c# 中的字符串类处理它),但我想你的问题涵盖了所有可能的可为空对象。
1)首先,关于?? 操作员在另一个答案中建议:它将允许您的代码与空对象一起使用,但在某些时候,您通常仍然需要处理空情况
例子:
if(a != null && a.b != null and a.b.c != null)
{
a.b.c.doSomething();
}
会变成
if(a?.b?.c? != null)
{
a.b.c.doSomething();
}
是的,它好一点,但并不能真正解决您的问题。
2)代码架构命题
我发现通常,allownull
意味着你的类有多种用途,并带有更多的状态。在此示例中,您的类可以表示“具有中间名的名称”或“没有全名的名称”。这里有一些关于代码架构的想法
通常,这意味着这个类做了太多的事情。当然,在您的示例中,班级很小。但在日常生活中,有一个可以为空的成员通常意味着类太大,支持的特性太多。一个班级应该只有一个目的。参见单一职责原则
在某些情况下(如示例中),您可能会认为您不想拆分课程,因为它是有意义的。然后你可以尝试看看是否有办法认为这个类具有相同的功能,而无需处理特殊情况。
在您的示例中,您可以执行以下操作:
public class Person
{
string[] FirstNames { get; set {if(value == null) FirstName } = new string[]{};
string LastName { get; set; } = string.Empty;
int Age { get; set; }
}
现在没有特殊情况了。所有函数都必须考虑一个名称数组,其大小可能为 1。
- 如果您真的非常想要一个空对象,您还可以将处理它的逻辑放在类中,并确保没有其他人访问该空对象。但这可能会导致在同一个班级中有大量的逻辑,所以这不是最好的解决方案
在你的例子中你可以做
public class Person
{
private string FirstName { get; set {if(value == null) FirstName } = string.Empty;
private string MiddleName { get; set; } = string.Empty;
string LastName { get; set; } = string.Empty;
int Age { get; set; }
public string getName()
{
// Here handle the null case
}
}
你可以看到这如何在课堂上增加了逻辑性。
- 最后,您可以阅读有关Null 对象模式的信息。此模式需要更多代码,但允许您替换
null
为对象的实际实例,当您在它们上调用函数时不会崩溃。我从来没有积极地使用过它,因为我通常设法通过上述原则获得更清晰的代码,但你可能会发现这很有用的一个案例。
下面是来自维基百科的 Null Object 实现示例
/* Null object pattern implementation:
*/
using System;
// Animal interface is the key to compatibility for Animal implementations below.
interface IAnimal
{
void MakeSound();
}
// Animal is the base case.
abstract class Animal : IAnimal
{
// A shared instance that can be used for comparisons
public static readonly IAnimal Null = new NullAnimal();
// The Null Case: this NullAnimal class should be used in place of C# null keyword.
private class NullAnimal : Animal
{
public override void MakeSound()
{
// Purposefully provides no behaviour.
}
}
public abstract void MakeSound();
}
// Dog is a real animal.
class Dog : IAnimal
{
public void MakeSound()
{
Console.WriteLine("Woof!");
}
}
/* =========================
* Simplistic usage example in a Main entry point.
*/
static class Program
{
static void Main()
{
IAnimal dog = new Dog();
dog.MakeSound(); // outputs "Woof!"
/* Instead of using C# null, use the Animal.Null instance.
* This example is simplistic but conveys the idea that if the Animal.Null instance is used then the program
* will never experience a .NET System.NullReferenceException at runtime, unlike if C# null were used.
*/
IAnimal unknown = Animal.Null; //<< replaces: IAnimal unknown = null;
unknown.MakeSound(); // outputs nothing, but does not throw a runtime exception
}
}
推荐阅读
- java - Spring Boot Embedded Undertow Server : java.io.IOException: UT000128: Remote peer closed connection before all data can be read
- java - axios 加载数据的速度不够快
- c# - Entity Framework 6 删除更新请求中不存在的所有行
- python - HTTPConnectionPool(host='xxxx', port=y):读取超时。(读取超时=无)在 nginx 服务器上?
- javascript - 在分页中过滤文本字段 Jquery
- html - 外部css文件在压缩文件夹中不起作用
- python - 如何更优雅地编写这些嵌套的 if 语句?
- python-3.x - Python Noob - 寻找反意大利面条的建议并避免冗余代码
- c++ - 将所有小写字母大写的程序不起作用
- css - 如何添加链接到浏览按钮闪亮的应用程序