在空字符串上调用ToString方法

36
为什么第二个代码会抛出异常,而第一个却不会?
string s = null;
MessageBox.Show(s);
MessageBox.Show(s.ToString());

更新 - 我可以理解的异常,让我困惑的是为什么第一部分没有显示异常。这与MessageBox无关,如下所示。

例如:

string s = null, msg;
msg = "Message is " + s; //no error
msg = "Message is " + s.ToString(); //error

第一部分似乎是将null隐式转换为空字符串。


安全的方法是查看如何对可能为空的对象执行ToString? - Michael Freidgeim
6
为了避免字符串或对象的null损坏,可以使用obj?.ToString() ?? "" - bvj
1
第二行出现错误是因为您在空字符串引用上调用了一个方法(.ToString())。这就是为令您得到异常的原因。 - Cosmin Sontu
10个回答

31

由于您无法在 null 引用上调用实例方法 ToString()

而且 MessageBox.Show() 可能被实现为忽略 null 并打印空消息框。


2
第一部分没问题,但我认为我的问题中的MessageBox部分有些分散注意力,因此我进行了更新。 - MartW

15

由于在Google搜索“c# toString null”时,这个问题排名相当高,因此我想补充一下,Convert.ToString(null)方法将返回一个空字符串,但是该字符串被消息框忽略。

然而,为了再次确认其他答案,您可以在这个例子中使用string.Concat("string", null)

编辑 - 根据HeyJude在下面的评论中提出的修改答案。就像指出的那样,类似Convert.ToString(null).Length的方法会抛出异常。


3
Convert.ToString(null) 返回一个null值,而不是空字符串(可以通过调用Convert.ToString(null) == null来验证,其返回值为true)。然而,传递一个null的变量确实等于null,因此object v = null; Convert.ToString(v) == string.Empty返回true(参见答案)。 - OfirD
1
@HeyJude - 发现得好,谢谢!我会相应地更新。 - RooiWillie

14

这是因为MessageBox.Show()是用pinvoke实现的,它调用了本地的Windows MessageBox()函数。该函数不介意lpText参数为空值。然而,C#语言对于纯.NET实例方法(如ToString)有更严格的规定,它总是会生成代码来验证对象是否为空。这篇博客文章提供了一些相关背景信息。


7
在幕后,concat 在您的后续问题/更新中被调用。例如:
string snull = null;

string msg = "hello" + snull;

// is equivalent to the line below and concat handles the null string for you.
string msg = String.Concat("hello", snull);

// second example fails because of the toString on the null object
string msg = String.Concat("hello", snull.ToString());

//String.Format, String.Convert, String.Concat all handle null objects nicely.

2
你能给我们展示一下这个“幕后操作”吗?因为我在mscorlib中肯定找不到它... - Robert Koritnik
那么你的意思是,字符串工具方法只是将其转换为“null”字符串,如果返回值为null? - Snehal Masne
使用ildasm.exe对编译后的示例代码进行反汇编,你就会看到James写的内容。 - Kobor42
对不起,但是当你有可空整数时,这非常宝贵。你可以写成字符串 s = "" + myInt; 感谢6年后! - Francois Girard

3

您正在尝试在空对象上执行ToString()方法。您需要一个有效的对象才能执行方法。


1

show 函数必须进行空值检查并处理。


正如其他人所说,异常是因为在空引用上调用了一个方法。 - Vinzz
1
确实,这是显而易见的部分。 - rcravens

1

0
因为第二次调用期望一个“s”的对象满足ToString()方法的请求,所以在调用.Show()之前,s.ToString()将会尝试调用一个方法而失败。
有趣的是,虽然.Show()被正确实现了,但是许多这样的方法都希望传入非空实例。通常,这时您会使用NullObject模式,这样调用者就不必处理这种行为。

0

可能Show方法处理了空值并且什么都没有显示。s-s.ToString()的第二个用法失败是因为你没有可运行的ToString方法。


0

正如你所说,这与MessageBox无关,而取决于ToString()方法的实现。

例如,看一下System.Runtime中的struct Nullable<T> where T : struct,其中包含一个可空枚举的示例:

[Serializable]
[NonVersionable] // This only applies to field layout
[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public partial struct Nullable<T> where T : struct

在 .NET 7 中,ToString() 的实现看起来像这样:

public override string? ToString() => hasValue ? value.ToString() : "";

因此,即使在null对象tc.TestEnumToString()上调用,以下代码在MessageBox.Show(s.ToString());引发异常。
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        var tc = new TestClass();
        MessageBox.Show(tc.TestEnum.ToString());

        string s = null;
        MessageBox.Show(s);
        MessageBox.Show(s.ToString());
    }
}

public enum TestEnum
{
    None = 0
}

public class TestClass
{

    public TestEnum? TestEnum { get; set; }

}

System.NullReferenceException:“对象引用未设置为对象的实例。”

enter image description here

System.Runtime 中查看 class String

[Serializable]
[NonVersionable] // This only applies to field layout
[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public sealed partial class String : IComparable, IEnumerable, IConvertible, IEnumerable<char>, IComparable<string?>, IEquatable<string?>, ICloneable

这里我们有另一个实现,它会抛出你看到的错误:

// Returns this string.
public override string ToString()
{
    return this;
}

然而,从 .NET 6< 开始,<Nullable>enable</Nullable> 对于新项目默认启用。

https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references

上面的代码将会给你两个警告:

string s = null; -> CS8600 将 null 文本或可能为 null 的值转换为非可空类型。

MessageBox.Show(s.ToString()); -> CS8602 可能为 null 的引用的解除引用。

enter image description here

按照以下方式重写代码,错误将消失:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        var tc = new TestClass();
        MessageBox.Show(tc.TestEnum.ToString());

        string? s = null;
        MessageBox.Show(s);
        if (s is not null)
        {
            MessageBox.Show(s.ToString());
        }
        
    }
}

public enum TestEnum
{
    None = 0
}

public class TestClass
{

    public TestEnum? TestEnum { get; set; }

}

或者简单地使用空值条件运算符?.,就像@mr R所写的那样:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        var tc = new TestClass();
        MessageBox.Show(tc.TestEnum.ToString());

        string? s = null;
        MessageBox.Show(s);
        MessageBox.Show(s?.ToString());
    }
}

public enum TestEnum
{
    None = 0
}

public class TestClass
{

    public TestEnum? TestEnum { get; set; }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接