为什么字符串的默认值为null而不是空字符串?

Mar*_*cel 207 c# string default-value

这是很烦人的测试我所有的字符串null之前,我可以放心地运用类似的方法ToUpper(),StartWith()等...

如果默认值string是空字符串,我就不用考了,我会觉得它是与其他价值类型,如更一致intdouble为例子.另外Nullable<String>有意义.

那么为什么C#的设计者选择使用null字符串的默认值呢?

注意:这与此问题有关,但更侧重于为什么而不是如何处理它.

Hab*_*bib 299

为什么字符串的默认值为null而不是空字符串?

因为string引用类型,所有引用类型的默认值是null.

在我可以安全地应用ToUpper(),StartWith()等方法之前,测试我的所有字符串是非常烦人的...

这与引用类型的行为一致.在调用实例成员之前,应该对空引用进行检查.

如果string的默认值是空字符串,我就不必测试了,我觉得它与其他值类型(例如int或double)更加一致.

将默认值分配给特定引用类型而不是null使其不一致.

另外Nullable<String>有意义.

Nullable<T>使用值类型.值得注意的是,Nullable原始.NET平台上没有引入这一事实,因此如果他们改变了这条规则,就会有很多破碎的代码.(图片来自@jcolebrand)

  • @HenkHolterman一个人可以实现大量的东西,但为什么会引入如此明显的不一致? (10认同)
  • @HenkHolterman和"Consistency"是对你的观点的反驳"字符串可以被视为与其他引用类型不同". (8认同)
  • @delnan:正在研究一种将字符串视为值类型并在dotnet上工作2年以上的语言,我同意Henk.我认为它是dotnet上的主要**FLAW**. (6认同)
  • @delnan - "为什么"是这里的问题. (4认同)

Dav*_*kle 39

Habib是对的 - 因为它string是一种参考类型.

但更重要的是,您不必null每次使用它都要检查.但是,ArgumentNullException如果某人将您的函数作为null参考传递,您可能应该抛出一个.

这就是事情 - NullReferenceException如果你试图调用.ToUpper()一个字符串,框架会为你抛出一个.请记住,即使您测试参数,这种情况仍然会发生,null因为传递给函数的对象上的任何属性或方法都可以作为参数进行评估null.

话虽这么说,检查空字符串或空值是常见的事情,因此它们提供String.IsNullOrEmpty()String.IsNullOrWhiteSpace()仅用于此目的.

  • 你不应该自己抛出一个`NullReferenceException`(http://msdn.microsoft.com/en-us/library/ms173163.aspx); 如果你的方法不能接受null引用,你抛出一个`ArgumentNullException`.此外,当你修复问题时,NullRef通常是更难诊断的例外之一,所以我认为不检查null的建议是非常好的. (29认同)
  • 抛出`ArgumentNullException`具有能够提供参数名称的额外好处.在调试过程中,这可以节省...错误,秒.但重要的是秒. (6认同)
  • @Andy"NullRef通常是诊断最困难的例外之一"我非常不同意,如果你记录它很容易找到并修复(只处理空案例). (3认同)
  • 谢谢,安迪.好点子.编辑相应. (2认同)
  • @DaveMarkle你可能也想包含IsNullOrWhitespace http://msdn.microsoft.com/en-us/library/system.string.isnullorwhitespace.aspx (2认同)

Tim*_*ter 23

你可以写一个扩展方法(为了它的价值):

public static string EmptyNull(this string str)
{
    return str ?? "";
}
Run Code Online (Sandbox Code Playgroud)

现在这安全地工作:

string str = null;
string upper = str.EmptyNull().ToUpper();
Run Code Online (Sandbox Code Playgroud)

  • **但请不要.**另一个程序员想要看到的最后一件事就是成千上万行的代码.EmptyNull()到处都是因为第一个人"害怕"异常. (97认同)
  • 评论是针对OP,而不是给你.虽然你的答案显然是正确的,但是应该强烈警告程序员提出这样的基本问题,不要将你的解决方案实际放入WIDE练习中,因为他们常常会这样做.您在答案中没有讨论过许多权衡,例如不透明,复杂性增加,重构困难,可能过度使用扩展方法,以及性能.有时(很多次)正确的答案不是正确的道路,这就是我评论的原因. (17认同)
  • @DaveMarkle:但显然这正是OP所期待的._"在我可以安全地应用ToUpper(),StartWith()等方法之前测试我的所有字符串是非常烦人的"_ (14认同)
  • 如果你遇到编写`.EmptyNull()`的麻烦,为什么不简单地使用`(str ??"")`来代替它?也就是说,我同意@ DaveMarkle评论中表达的观点:你可能不应该这样做.`null`和`String.Empty`在概念上是不同的,你不一定能把它们视为另一个. (7认同)
  • @Andy:没有进行正确的空值检查的解决方案是正确检查空值,而不是对问题进行创可贴. (5认同)
  • 有时这样看起来很干净的扩展方法很好,不需要打值?"到处都是. (3认同)
  • 当然你可以用这个(坏的)方向进一步使用`public static string ToUpperSafe(this string str){return str == null?null:str.ToUpper(); }等等...... (3认同)
  • 很多人似乎都有你的意见.请注意,我并不鼓励用`EmptyNull`替换`string`的所有出现.这只是对OP要求的直接回答.许多程序员知道他们正在做什么或正在自己工作(就像我一样).顺便说一句,在这里我发现了一个针对这个问题的问题:stackoverflow.com/questions/8536740/... (2认同)

rus*_*ema 16

您也可以(从VS2015和新编译器开始)使用以下内容

string myString = null;
string result = myString?.ToUpper();
Run Code Online (Sandbox Code Playgroud)

字符串结果将为null.

  • 另一种选择 - `public string Name {get; 组; } = string.Empty;` (3认同)
  • 正确地说,从 c# 6.0 开始,IDE 的版本与它无关,因为这是一种语言功能。 (2认同)

Ner*_*rve 14

空字符串和空值根本不同.null表示缺少值,空字符串表示空值.

编程语言对变量的"值"进行假设,在本例中为空字符串,与使用任何其他不会导致空引用问题的值初始化字符串一样好.

此外,如果将句柄传递给该字符串变量到应用程序的其他部分,那么该代码将无法验证您是否故意传递了空白值,或者您忘记填充该变量的值.

另一个会出现问题的场合是字符串是某个函数的返回值.由于string是一个引用类型,并且在技术上可以将值设置为null并且两者都为空,因此该函数在技术上也可以返回null或空(没有什么可以阻止它这样做).现在,由于存在2个"缺少值"的概念,即空字符串和空值,所有使用此函数的代码都必须进行2次检查.一个用于空,另一个用于null.

简而言之,对于单个状态只有1个表示总是好的.有关empty和nulls的更广泛讨论,请参阅下面的链接.

https://softwareengineering.stackexchange.com/questions/32578/sql-empty-string-vs-null-value

处理用户输入时为NULL与Empty

  • 你怎么看到这种差异,比如说在文本框中?用户是否忘记在字段中输入值,还是故意将其留空?编程语言中的空格确实具有特定含义; 未分配.我们知道它没有值,这与数据库null不同. (2认同)

sup*_*cat 7

根本原因/问题是CLS规范的设计者(定义语言如何与.net交互)没有定义一种方法,通过该方法,类成员可以指定必须直接调用它们,而不是通过callvirt调用,而不需要调用者执行空引用检查; 它也没有提供任何不受"正常"拳击影响的定义结构.

如果CLS规范定义了这样一种方法,那么.net就有可能始终遵循公共对象模型(COM)建立的线索,在该线索下,空字符串引用被认为在语义上等同于空字符串,而对于其他用户定义的不可变类类型,它们应具有值语义,以同样定义默认值.从本质上讲,会发生的事情将是每个成员String,例如Length写成类似的东西[InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} }.这种方法可以为应该表现得像值的事物提供非常好的语义,但是由于实现问题需要存储在堆上.这种方法最大的困难在于这些类型之间转换的语义Object可能会有点模糊.

另一种方法是允许定义不继承的特殊结构类型,Object而是使用自定义装箱和拆箱操作(可以转换为/从某些其他类类型转换).在这种方法下,会有一个类型NullableString,其行为与现在的字符串一样,以及一个自定义框结构类型String,它将包含一个Value类型的私有字段String.尝试将a转换StringNullableStringObject将返回Value非null,或者String.Empty如果为null.尝试转换String为对NullableString实例的非null引用将存储引用Value(如果长度为零,则可能存储null); 转换任何其他引用将引发异常.

尽管字符串必须保存在堆上,有概念没有理由他们不应该表现得就像是有一个非空默认值的值类型.将它们存储为保持引用的"普通"结构对于将它们用作类型"字符串"的代码是有效的,但是在转换为"对象"时会增加额外的间接层和低效率.虽然我没有预见.net会在这个晚期添加上述任何一个功能,但未来框架的设计者可能会考虑将它们包括在内.

  • @JonofAllTrades:处理数字的代码必须有一个带外方法来区分默认值零和"未定义".实际上,使用字符串和数字的可空处理代码必须使用一种方法用于可空字符串,另一种方法用于可空字符串.即使可空类的类型`string`比`Nullable <string>`更有效,但是必须使用"更有效"的方法比能够对所有可空数据数据库值使用相同的方法更麻烦. (2认同)

Ame*_*ILI 6

??也许如果您在分配字符串变量时使用运算符,它可能会对您有所帮助。

string str = SomeMethodThatReturnsaString() ?? "";
// if SomeMethodThatReturnsaString() returns a null value, "" is assigned to str.
Run Code Online (Sandbox Code Playgroud)


Hen*_*man 5

因为字符串变量是引用,而不是实例.

默认情况下将其初始化为Empty是可能的,但它会在整个板上引入很多不一致的地方.

  • 没有特别的原因`string`必须是引用类型.可以肯定的是,组成字符串的实际字符肯定必须存储在堆上,但考虑到字符串在CLR中已经具有的专用支持量,将System.String`作为一个延伸是不可能的.一个值类型,其中包含一个类型为`HeapString`的私有字段`Value`.该字段将是一个引用类型,默认为"null",但是`Value`字段为null的`String`结构将表现为空字符串.这种方法的唯一缺点是...... (3认同)

Son*_*nül 5

为什么C#的设计者选择使用null作为字符串的默认值?

因为字符串是引用类型,所以引用类型是默认值null.引用类型的变量存储对实际数据的引用.

我们default在这种情况下使用关键字;

string str = default(string); 
Run Code Online (Sandbox Code Playgroud)

str是a string,所以它是引用类型,所以默认值是null.

int str = (default)(int);
Run Code Online (Sandbox Code Playgroud)

str是一个int,所以它是一个值类型,所以默认值是zero.