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

时间:2013-01-15 12:17:12

标签: c# string default-value

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

如果string的默认值是空字符串,我就不必测试了,我觉得它与int或{{1}等其他值类型更加一致} 例如。 另外double会有意义。

那么为什么C#的设计者选择使用Nullable<String>作为字符串的默认值?

注意:这与this question有关,但更侧重于为什么而不是如何处理它。

14 个答案:

答案 0 :(得分:294)

  

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

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

  

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

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

  

如果string的默认值是空字符串,我就不会   测试,我觉得它与另一个更加一致   值类型,如int或double,例如。

将默认值分配给null以外的特定引用类型会使其不一致

  

另外Nullable<String>会有意义。

Nullable<T>适用于值类型。值得注意的是Nullable未在原始.NET platform上引入,因此如果他们更改了该规则,就会有很多损坏的代码。( Courtesy @jcolebrand)< / em>的

答案 1 :(得分:40)

Habib是对的 - 因为string是引用类型。

但更重要的是,每次使用时,都必须检查null。如果有人通过您的函数ArgumentNullException引用,您可能应该抛出null

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

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

答案 2 :(得分:23)

你可以写一个extension method(为了它的价值):

public static string EmptyNull(this string str)
{
    return str ?? "";
}

现在这可以安全地运作:

string str = null;
string upper = str.EmptyNull().ToUpper();

答案 3 :(得分:14)

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

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

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

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

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

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

NULL vs Empty when dealing with user input

答案 4 :(得分:13)

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

string myString = null;
string result = myString?.ToUpper();

字符串结果将为null。

答案 5 :(得分:6)

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

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

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

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

答案 6 :(得分:5)

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

默认情况下将其初始化为空是可能的,但它会在全面引入许多不一致的地方。

答案 7 :(得分:5)

  

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

由于字符串是引用类型,因此引用类型的默认值为null。引用类型的变量存储对实际数据的引用。

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

string str = default(string); 

strstring,因此它是引用类型,因此默认值为null

int str = (default)(int);

strint,因此它是值类型,因此默认值为zero

答案 8 :(得分:4)

  

如果string的默认值是空字符串,我就不必测试

错误!更改默认值并不会改变它是引用类型的事实,并且某人仍然可以明确引用设置为null

  

另外Nullable<String>会有意义。

真的。对于任何引用类型不允许null更有意义,而是要求Nullable<TheRefType>用于该功能。

  

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

与其他参考类型的一致性。现在,为什么要在引用类型中允许null?可能让它感觉像C一样,尽管这是一种可疑的设计决策,也提供Nullable的语言。

答案 9 :(得分:3)

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

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

答案 10 :(得分:2)

由于string是引用类型,引用类型的默认值为null。

答案 11 :(得分:0)

也许string关键字让您感到困惑,因为它看起来与任何其他值类型声明完全相同,但它实际上是System.String的别名,如{{3}中所述}}。
此外,Visual Studio中的深蓝色和小写的第一个字母可能误导为认为它是struct

答案 12 :(得分:0)

Nullable类型直到2.0才出现。

如果在语言的开头有可空类型,那么字符串将是不可为空的字符串?本来可以为空的。但他们无法做到向后兼容。

很多人都在谈论ref-type或者不是ref类型,但是string是一个不同寻常的类,并且已经找到解决方案使其成为可能。

答案 13 :(得分:0)

自从您提到ToUpper()以来,这种用法就是我找到此线程的方式,我将共享此快捷方式(字符串??“”)。ToUpper():

    private string _city;
    public string City
    {
        get
        {
            return (this._city ?? "").ToUpper();
        }
        set
        {
            this._city = value;
        }
    }

似乎比:

        if(null != this._city)
        { this._city = this._city.ToUpper(); }