Why do the overloads of String.Format exist?
Asked Answered
S

2

18

I was using Reflector to look at the implementation of String.Format and had always been under the impression that the overloads of String.Format that took 1, 2 & 3 arguments were optimized versions of the method that takes an object array. However, what I found was that internally they create an object array and then call a method that takes an object array.

1 arg

public static string Format(string format, object arg0)
{
    if (format == null)
    {
        throw new ArgumentNullException("format");
    }
    return Format(null, format, new object[] { arg0 });
}

2 args

public static string Format(string format, object arg0, object arg1)
{
    if (format == null)
    {
        throw new ArgumentNullException("format");
    }
    return Format(null, format, new object[] { arg0, arg1 });
}

3 args

public static string Format(string format, object arg0, object arg1, object arg2)
{
    if (format == null)
    {
        throw new ArgumentNullException("format");
    }
    return Format(null, format, new object[] { arg0, arg1, arg2 });
}

Object array

public static string Format(string format, params object[] args)
{
    if ((format == null) || (args == null))
    {
        throw new ArgumentNullException((format == null) ? "format" : "args");
    }
    return Format(null, format, args);
}

Internally they all end up using the same code and so using the 1, 2 & 3 argument versions are no faster than the object array version.

So my question is - why do they exist?

When you use the object array version with a comma separated list of values, the compiler automatically converts the arguments into an object array because of the params/ParamArray keyword which is essentially what the 1, 2 & 3 versions do, so they seem redundant. Why did the BCL designers add these overloads?

Shupe answered 9/5, 2010 at 5:35 Comment(0)
P
8

One reason, as Hans mentions, is that creating an array is a lot of unnecessary overhead in most common cases of formatting a string. This saves space in the EXE.

Another reason is that not all languages support variadic functions (use of params in C#). This allows users of those languages to avoid array creation for the most common cases of string formatting. This saves a lot for languages that don't have simple syntax for array creation and initialization.

Potboiler answered 9/5, 2010 at 5:53 Comment(4)
"creating an array is a lot of unnecessary overhead" The array is created inside the method how is that any different in terms of "overhead"?Pandolfi
@ChrisMarisic: It's not the memory use or GC overhead of creating the arrays that's the problem; it's the extra opcodes. Calling string.Format with just a single string replacement requires only 3 IL opcodes, whereas it would require 10 opcodes if the overload didn't exist. Each additional parameter requires only 1 more opcode if it can use an overload, versus 4 more opcodes if it uses an array. Since a method cannot be inlined if it is too many opcodes, this has major implications.Potboiler
So the overloads are to increase the probability that Format call itself is inlined where it is used? Will it never be inlined if it has to use the params overload?Pandolfi
The overloads increase the probability that the method containing the Format call will be inlined.Potboiler
H
3

You are forgetting about the code in the app required to make the call. Creating the array and filling it takes a lot more IL than just passing 3 args.

Horsecar answered 9/5, 2010 at 5:50 Comment(2)
You're absolutely right. I'd not considered that. I guess you could say that those methods optimize calling code size rather than performance which I'd always thought they were there for.Shupe
So the overloads make sense if you're calling it in direct IL?Pandolfi

© 2022 - 2024 — McMap. All rights reserved.