我经常看到class
喜欢
public class Person
{
string FirstName { get; set; }
string MiddleName { get; set; }
string LastName { get; set; }
int Age { get; set; }
}
其中一个属性(在此示例中为MiddleName
(因为某些人出生时没有使用中间名))被允许为null
。我认为这是错误的,因为该类的所有使用者都必须知道属性“可能”为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
-值类型的类,例如
public class Something
{
public int SomeInt { get; set; }
public double? SomeDouble { get; }
}
出于同样的原因。
但是我想知道这是否像“ OOP过度设计”,因为它显然增加了很多开发时间。
答案 0 :(得分:1)
我看到当前的答案是关于String的(例如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)代码体系结构主张
我发现通常,允许null
意味着您的课程有多种用途,并带有更多状态。在此示例中,您的类可以表示“带有中间名的名称”或“没有全名的名称”。这是有关代码体系结构的一些想法
通常,这意味着该类做了太多事情。当然,在您的示例中,班级很小。但是在日常生活中,拥有可以为null的成员通常意味着该类太大并且支持太多功能。一堂课只有一个目的。参见Single Responsibility Principle
在某些情况下(例如在示例中),您可能会认为您不想拆分该类,因为它按原样有意义。然后,您可以尝试查看是否有一种方法可以认为此类具有相同的功能,而无需处理特殊情况。
在您的示例中,您可以执行以下操作:
public class Person
{
string[] FirstNames { get; set {if(value == null) FirstName } = new string[]{};
string LastName { get; set; } = string.Empty;
int Age { get; set; }
}
现在没有特殊情况。所有函数都必须考虑一个名称数组,该名称数组的大小可以为一个。
在您的示例中,您可以
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 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
}
}
答案 1 :(得分:0)
有时候,对于确定无疑的null很有用,例如当您有一个表示一组问题答案的对象时,例如布尔? TrueFalseAnswer == null表示未回答问题,而不是默认为false,或者字符串name == null表示未填写名称字段,等等。
就您的课程而言,您至少可以将它们初始化为空字符串。
public class Person
{
string FirstName { get; set {if(value == null) FirstName } = string.Empty;
string MiddleName { get; set; } = string.Empty;
string LastName { get; set; } = string.Empty;
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#使检查它们变得非常容易。