我有一个简单的问题,但我很惊讶。
此代码有效:
int itemID = 1;
string dirPath = @"C:\" + itemID + @"\abc";
为什么我不必itemID.ToString()
在这种情况下做?
来自MSDN
当一个或两个操作数都是字符串类型时,二元 + 运算符执行字符串连接。如果字符串连接的操作数为 null,则替换为空字符串。否则,通过调用从类型 object 继承的虚拟 ToString 方法,将任何非字符串参数转换为其字符串表示形式。如果 ToString 返回 null,则替换为空字符串。
根据其他答案中的一些评论,稍微扩展我的答案......
这个过程不仅仅是“语法糖”或便利。它是称为运算符重载的核心 C# 语言功能的结果。在 + 运算符和字符串 + 重载的情况下,提供重载作为抽象字符串类内部的一种手段,这是良好设计原则的核心基础。+ 字符串重载通过确保它永远不会返回空值来提供类型安全,而不是为无法使用该.ToString()
方法转换的任何操作数返回一个空字符串。但是,即使自定义复杂类型(不仅仅是基元)也可以添加到字符串中,假设它们具有.ToString()
重载,而不会知道 String 类型的实现有什么不同。
运算符重载是一个主要的语言特性,更多的人应该学会利用它的力量。
+
in 字符串连接被转换为调用,它在每个对象上string.Concat
内部调用无参数。ToString
该方法通过调用该对象的无参数 ToString 方法来连接 args 中的每个对象;它不添加任何分隔符。
编辑:
+ 运算符有许多重载。其中三个如下:
运算符 +(字符串 a,字符串 b)
运算符 +(字符串 a,对象 b)
运算符 +(对象 a,字符串 b)
您的代码正在使用运算符的第二个重载。
该运算符将在确定为匹配项后将其转换为对 的调用string.Concat
,该调用可以将任意数量的对象(类型object
为 )作为其参数。
在 的定义中string.Concat
,它将调用ToString
所有参数(首先检查它们是否为空)以获取它们的字符串值,这就是将被连接的内容。
由于所有这些,您始终可以将任何对象与字符串连接,并且它将使用该对象的ToString
方法进行编译和执行。
因为它是由编译器自动完成的。
基本上:
string a = "abc" + 1;
编译为:
string a = string.Concat((object)"abc", (object)1);
它只是语法糖。虽然我个人不喜欢自动转换。
参考:http Concat
:
//msdn.microsoft.com/en-us/library/kbseaaft.aspx
一个非常有趣的问题。
让我们看看您的示例的 IL 代码:
ldstr "C:\\"
ldloc.0
box int
ldstr "\\abc"
call string System.String.Concat(object, object, object)
后面调用的函数是 System.String.Concat,它需要 3 个对象!
让我们深入了解 .NET 源代码:
public static String Concat(Object arg0, Object arg1, Object arg2) {
Contract.Ensures(Contract.Result<String>() != null);
Contract.EndContractBlock();
if (arg0 == null)
{
arg0 = String.Empty;
}
if (arg1==null) {
arg1 = String.Empty;
}
if (arg2==null) {
arg2 = String.Empty;
}
return Concat(arg0.ToString(), arg1.ToString(), arg2.ToString());
}
如您所见ToString()
,它将被称为但稍后!这令人印象深刻的是微软人正在做的事情!
这是一个语言特性。ToString()
在使用运算符进行字符串连接时在对象内部调用+
。
这样做的根本原因仅仅是对开发人员的方便(并且可能由于ToString()
调用较少而增加了可读性,但它也可能诱使您认为对象是字符串,而实际上它可以是任何东西)。
对于 C#,可能采用了 Java 的行为,这是相同的。问一个问题可能很有趣,为什么首先在 Java 中引入了这一点。