为什么String.Format是静态的?

40

比较

String.Format("Hello {0}", "World");

"Hello {0}".Format("World");

为什么 .Net 设计者选择使用静态方法而不是实例方法?您的看法是什么?
22个回答

50
因为Format方法与字符串的当前值无关,这对所有字符串方法都是正确的,因为.NET字符串是不可变的。如果它是非静态的,那么你需要一个字符串作为基础。它确实需要一个格式化字符串。我认为这只是.NET平台中许多设计缺陷的另一个例子(我并不是指这个作为攻击;我仍然认为.NET框架比大多数其他框架更优秀)。

2
为什么这不是被接受的答案? - Tim
1
@tim:因为我认为安德鲁的答案更好。 :-) - Jakub Šturc
4
我其实也更喜欢被接受的答案,因为它完美地回答了这个问题。我的回答更像是对一些关于静态函数设计的假设的评论。 - Konrad Rudolph
1
反射为“不可变”提供了一个新的同义词:可变... :P - Humphrey Bogart

30

我实际上不知道答案,但我猜测它与直接在字符串字面量上调用方法的方面有关。

如果我没记错(我没有实际验证这一点,因为我手头没有旧版IDE),早期版本的C# IDE在IntelliSense中检测字符串字面量的方法调用时存在问题,这对API的可发现性产生了很大影响。如果是这种情况,键入以下内容将不会给您任何帮助:

"{0}".Format(12);
如果您被迫输入

new String("{0}").Format(12);

很明显,将Format方法作为实例方法而不是静态方法并没有任何优势。.NET库由许多与我们提供MFC的相同的人设计,特别是String类与MFC中的CString类非常相似。MFC确实具有一种实例格式化方法(使用printf样式的格式代码而不是.NET的花括号样式),但这很麻烦,因为没有CString字面量。因此,在我工作的MFC代码库中,我经常看到这种情况:

CString csTemp = "";
csTemp.Format("Some string: %s", szFoo);

这让人感到痛苦。(我并不是说上面的代码即使在MFC中也是一种好的做法,只是似乎大多数项目开发人员都学会了如何使用CString::Format)。出于这种背景,我可以想象API设计者试图避免再次出现这种情况。


8
所以它被设计成那样是因为IDE的设计不称职? - Humphrey Bogart
1
那么,既然IDE更好了,为什么他们不会后来添加"".Format形式呢?据我所知,这并不会破坏任何东西。 - chtenb

9

我想你需要对它进行特别的处理,但是像其他人所说,由于隐含的语义,将String.Format设置为静态更有意义。考虑以下内容:

"Hello {0}".Format("World"); // this makes it sound like Format *modifies* 
                             // the string, which is not possible as 
                             // strings are immutable.

string[] parts = "Hello World".Split(' ');    // this however sounds right, 
                                             // because it implies that you 
                                             // split an existing string into 
                                             // two *new* strings.

8
我认为你的第二个例子对于像 "a b c".Replace("a", "kitty") 这样的情况并不适用。 - davidtbernal

8
当我升级到VS2008和C#3时,我第一件要做的事情就是这样:
public static string F( this string format, params object[] args )
{
    return String.Format(format, args);
}

现在我可以将我的代码从以下形式改变为

String.Format("Hello {0}", Name);

为了

"Hello {0}".F(Name);

我曾经喜欢这种方式。但现在(2014年)我不再使用它,因为每次创建新项目或者链接到一些工具库时都需要重新添加,这只会带来麻烦。

至于为什么.NET设计者选择了它?谁知道呢。这似乎完全是主观的。我猜可能是:

  • 模仿Java
  • 当时编写它的人主观上更喜欢它。

我找不到其他合理的原因。


2
我认为扩展方法不是正确的方法。它会给读者造成困惑。我相信在C#中我们只能使用静态版本。 - Jakub Šturc
1
这是主观的,@Jakub。对我来说- 当我看到花括号时,我立刻知道它是什么。 - Arnis Lapsa
2
我不确定什么是"wft时刻",但这与扩展方法的任何其他用法有何不同? - Ken
1
我将此放在System命名空间下,其中包含String。 - Kugel

6

我认为这是因为Format不是接受一个字符串,而是"格式化字符串"。大多数字符串等于"Bob Smith"、"1010 Main St"或者其他一些东西,而不是"Hello {0}"。通常只有在创建另一个字符串的模板(比如工厂方法)时才会使用格式化字符串,因此它适用于静态方法。


这似乎是目前为止最好的答案。但是,如果它应该与一个字符串“不同”,那么为什么要把它放在String类中... - Thomas Ahle

5

我认为这是因为它是一个创建方法(不确定是否有更好的名称)。它所做的就是接受您提供的内容并返回单个字符串对象。它不对现有对象进行操作。如果它是非静态的,您需要先有一个字符串。


4

据我所知,.NET方法比Java方法更早(Java只在版本5中才有它)。 - Joachim Sauer
什么?Java 1.5:http://java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html#format(java.lang.String,%20java.lang.Object...) - jm.
@jm:由于某些奇怪的营销原因,Java 1.5和Java 5是同一个东西。 - Simon Nickerson

4
.NET字符串是不可变的,因此拥有实例方法毫无意义。
按照这种逻辑,字符串类不应该有任何返回修改后对象的实例方法,但它确实有很多(如Trim、ToUpper等)。此外,框架中的许多其他对象也是如此。
我同意,如果他们将其作为实例方法,则“Format”似乎会是一个糟糕的名称,但这并不意味着功能不应该是实例方法。
为什么不这样呢?这与.NET框架的其余部分一致
"Hello {0}".ToString("Orion");

3

因为Format方法与字符串的当前值无关。字符串的值不会被使用。它接受一个字符串并返回一个字符串。


2

这是为了避免与.ToString()方法混淆。

例如:

double test = 1.54d;

//string.Format pattern
string.Format("This is a test: {0:F1}", test );

//ToString pattern
"This is a test: " + test.ToString("F1");

如果Format是字符串的实例方法,这可能会导致混淆,因为模式不同。
String.Format()是将多个对象转换为格式化字符串的实用方法。
字符串的实例方法对该字符串执行某些操作。
当然,你也可以这样做:
public static string FormatInsert( this string input, params object[] args) {
    return string.Format( input, args );
}

"Hello {0}, I have {1} things.".FormatInsert( "world", 3);

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