在我可以安全地应用 ToUpper()
、StartWith()
等方法之前,测试所有字符串是否为 null
很烦人。
如果默认值的 string
是空字符串,则我不必进行测试,并且这会更加符合其他值类型(例如 int
或 double
)的一致性。此外,Nullable<String>
也是有意义的。
那么,为什么 C# 的设计者选择使用 null
作为字符串的默认值呢?
注意:这与这个问题相关,但更专注于为什么而不是该怎么做。
在我可以安全地应用 ToUpper()
、StartWith()
等方法之前,测试所有字符串是否为 null
很烦人。
如果默认值的 string
是空字符串,则我不必进行测试,并且这会更加符合其他值类型(例如 int
或 double
)的一致性。此外,Nullable<String>
也是有意义的。
那么,为什么 C# 的设计者选择使用 null
作为字符串的默认值呢?
注意:这与这个问题相关,但更专注于为什么而不是该怎么做。
Nullable<T>
适用于值类型。需要注意的是,Nullable
在最初的.NET平台上并未被引入,因此如果他们改变了这个规则,会有很多代码出现错误。(来自@jcolebrand的礼貌提醒)
Habib是对的--因为string
是一个引用类型。
但更重要的是,你不需要每次使用它都检查是否为null
。虽然你应该在有人把一个null
引用传递给你的函数时抛出一个ArgumentNullException
异常。
事实上,如果你试图在一个字符串上调用.ToUpper()
,框架会自动为你抛出NullReferenceException
异常。请记住,即使你测试了参数的null
值,由于传递给你的函数作为参数的对象的任何属性或方法可能会计算为null
,这种情况仍然可能发生。
话虽如此,检查空字符串或null值是一种常见的做法,所以他们提供了String.IsNullOrEmpty()
和String.IsNullOrWhiteSpace()
来解决这个问题。
ArgumentNullException
,而不是自己抛出NullReferenceException
(http://msdn.microsoft.com/en-us/library/ms173163.aspx)。此外,当你修复问题时,`NullRef`通常是最难诊断的异常之一,所以我认为不检查空引用的建议并不是一个很好的建议。 - AndyArgumentNullException
的额外好处在于能够提供参数名称。在调试期间,这可以节省... 嗯,几秒钟时间。但这些是重要的几秒钟。 - Kos你可以编写一个扩展方法(如果值得的话):
public static string EmptyNull(this string str)
{
return str ?? "";
}
现在这个功能是安全的:
string str = null;
string upper = str.EmptyNull().ToUpper();
(str ?? "")
呢?话虽如此,我同意 @DaveMarkle 评论中表达的情感:你可能不应该这样做。null 和 String.Empty 在概念上是不同的,并不能将它们视为相同处理。 - user从 C# 6.0 开始,您也可以使用以下内容
string myString = null;
string result = myString?.ToUpper();
字符串的结果将为 null。
public string Name { get; set; } = string.Empty;
该代码表示一个公共属性 "Name",它被定义为字符串类型,并且具有默认值为空字符串。 - Jaja Harris空字符串和null是根本不同的。Null是缺少值,而空字符串是一个没有值的值。
编程语言对变量“值”的假设,特别是空字符串,在这种情况下,将与使用任何其他不会引起空引用问题的值初始化字符串一样好。
此外,如果将该字符串变量的句柄传递给应用程序的其他部分,则该代码将无法验证您是否有意传递了空白值或者您是否忘记了填充该变量的值。
另一个可能会出现问题的场合是字符串是某些函数返回的值。由于字符串是引用类型并且在技术上可以具有null和empty作为值,因此该函数也可以在技术上返回null或empty(没有阻止它这样做)。现在,由于“值的缺失”有两种概念,即空字符串和null,因此所有使用此函数的代码都必须进行两个检查。一个是为空,另一个是为null。
简而言之,始终只有一个表示单个状态的表示形式是很好的。有关空值和null的更广泛讨论,请参见下面的链接。
https://softwareengineering.stackexchange.com/questions/32578/sql-empty-string-vs-null-value
null
表示空字符串。.net 有很多种实现类似语义的方式可供选择,尤其是考虑到 String
具有一些独特的特性 [例如,它和两个数组类型是唯一的分配大小不是常量的类型]。 - supercatcallvirt
调用,而不需要调用方执行空引用检查;也没有提供一种定义结构的方法,这些结构不会受到“正常”装箱的影响。String
成员(例如Length
)都将被编写为以下内容:[InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} }
。这种方法对于应该像值一样行事但由于实现问题需要存储在堆上的东西来说,提供了非常好的语义。这种方法的最大困难在于这种类型和Object
之间的转换语义可能会变得有点混乱。Object
,而是具有自定义的装箱和拆箱操作(它们将转换为/从某些其他类类型)。在这种方法下,将存在一个类类型NullableString
,其行为方式与当前的字符串相同,并且一个自定义装箱的结构类型String
,它将包含一个Value
字段,该字段的类型为String
。如果尝试将String
转换为NullableString
或Object
,则如果非空,则返回Value
,否则返回String.Empty
。尝试将非空引用强制转换为String
时,NullableString
实例会将该引用存储在Value
中(如果长度为零,则可能存储null);尝试将任何其他引用强制转换为String
时,都会抛出异常。Nullable<>
,字符串可以重新实现为值类型;我无法评论这种选择的成本和收益。 - user565869string
比Nullable<string>
更高效,但是必须使用"更高效"的方法会比能够将所有可空的数据库值都用同一种方式处理更加繁琐。 - supercat为什么C#的设计者选择使用null作为字符串的默认值?
因为字符串是引用类型,引用类型的默认值是null
。引用类型的变量存储对实际数据的引用。
让我们为这种情况使用default
关键字;
string str = default(string);
str
是一个字符串(string
)类型,因此它是一个引用类型(reference type),因此默认值为 null
。
int str = (default)(int);
str
是一个 int
,因此它是一个值类型,所以默认值是 零
。
??
运算符来帮助您。string str = SomeMethodThatReturnsaString() ?? "";
// if SomeMethodThatReturnsaString() returns a null value, "" is assigned to str.
string
必须是引用类型。确实,组成字符串的实际字符必须存储在堆上,但鉴于字符串在CLR中已经拥有专门的支持,将System.String
作为一个值类型并具有单个私有字段Value
(类型为HeapString
)也不算太难。该字段将是一个引用类型,并默认为null
,但Value
字段为null
的String
结构体将表现为空字符串。这种方法的唯一缺点是... - supercatString
转换为Object
会导致在堆上创建一个装箱的String
实例,而不是仅仅复制对HeapString
的引用。 - supercat.Length
等方法/属性上添加空值检查之外。这样,持有空引用的实例不会尝试取消引用它,而是表现为适合空字符串的行为。无论框架是否更好或更差,如果想要default(string)
成为空字符串... - supercatstring
作为一个值类型封装在引用类型字段上可能是需要对.NET的其他部分进行最少更改的方法[的确,如果愿意接受从String
到Object
的转换创建额外的装箱项,那么可以简单地让String
成为一个具有Char[]
类型的字段的普通结构体,它永远不会被公开暴露]。我认为拥有一个HeapString
类型可能更好,但在某些方面,持有Char[]
的值类型字符串会更简单。 - supercat