views:

1177

answers:

4

How many parameters can you pass to a string.Format() method?

There must be some sort of theoretical or enforced limit on it. Is it based on the limits of the params[] type or the memory usage of the app that is using it or something else entirely?

+14  A: 

Not as far as I know...

well, the theoretical limit would be the int32 limit for the array, but you'd hit the string length limit long before that, I guess...

Just don't go mad with it ;-p It may be better to write lots of small fragments to (for example) a file or response, than one huge hit.

edit - it looked like there was a limit in the IL (0xf4240), but apparently this isn't quite as it appears; I can make it get quite large (2^24) before I simply run out of system memory...


Update; it seems to me that the bounding point is the format string... those {1000001}{1000002} add up... a quick bit of math (below) shows that the maximum useful number of arguments we can use is 206,449,129:

    long remaining = 2147483647;// max theoretical format arg length
    long count = 10; // i.e. {0}-{9}
    long len = 1;
    int total = 0;
    while (remaining >= 0) {
        for(int i = 0 ; i < count && remaining >= 0; i++) {
            total++;
            remaining -= len + 2; // allow for {}
        }
        count *= 10;
        len++;
    }

    Console.WriteLine(total - 1);
Marc Gravell
wow, an arbitrary undocumented limit... I don't like that.+1 for research.
Michael Meadows
Well, I'm still working on it - not sure if it is right... still playing...
Marc Gravell
The CLR doesn't not support objects grater than 2GiB. Because the format method pre-allocates a StringBuilder with 8 characters for each argument one theoretical limit would be int.MaxValue/(sizeof(char)*8) which equals 134,217,728
John Leidegren
It's ok, I'll post it as an answer for John . . . JOKING! I'm only joking :) +1 Excellent answer Marc
Binary Worrier
@John - are you sure about the 8 char detail? There is only one StringBuilder, no matter the number of arguments...
Marc Gravell
Yes! Goto string.Format(IFormatProvider,string format, params object[] args) it tries to allocate this new StringBuilder(format.Length + (args.Length * 8)); which eventually calls FastAllocateString(int capacity) I can only assume that it will allocate 2 bytes of every character.
John Leidegren
@John - yup, you're right... you really should post as an answer, you know... your number is the lowest, ergo the limit...
Marc Gravell
@Marc - OK, got an answer for you, its 107713904.
John Leidegren
+3  A: 

Considering that both the limit of the Array class and the String class are the upper limit of Int32 (documented at 2,147,483,647 here: Int32 Structure), it is reasonable to believe that this value is the limit of the number string of format parameters.

Update Upon checking reflector, John is right. String.Format, using the Red Gate Reflector, shows the ff:

public static string Format(IFormatProvider provider, string format, params object[] args)
{
    if ((format == null) || (args == null))
    {
        throw new ArgumentNullException((format == null) ? "format" : "args");
    }
    StringBuilder builder = new StringBuilder(format.Length + (args.Length * 8));
    builder.AppendFormat(provider, format, args);
    return builder.ToString();
}

The format.Length + (args.Length * 8) part of the code is enough to kill most of that number. Ergo, '2,147,483,647 = x + 8x' leaves us with x = 238,609,294 (theoretical).

It's far less than that of course; as the guys in the comments mentioned the string hitting the string length limit earlier is quite likely.

Maybe someone should just code this into a machine problem! :P

Jon Limjap
That's actually incorrect, check the comment left on Marc's answer...
John Leidegren
Dude, you really should just provide an answer ;)
Jon Limjap
Besides - in both cases, you're more hampered by the format string.... {1000000}{1000001}{1000002} etc... it adds up quickly...
Marc Gravell
@Marc - Yeah, that format string itself should hit the 2 GiB limit a lot sooner...
John Leidegren
@Jon Limjap - well it's sort of redundant, I like comments ;)
John Leidegren
In fact, at 206,449,129 (see update)
Marc Gravell
That's great Marc! :)
Jon Limjap
+3  A: 

Expanding on Marc's detailed answer.

The only other limitation that is important is for the debugger. Once you pass a certain number of parameters directly to a function, the debugger becomes less functional in that method. I believe the limit is 64 parameters.

Note: This does not mean an array with 64 members, but 64 parameters passed directly to the function.

You might laugh and say "who would do this?" which is certainly a valid question. Yet LINQ makes this a lot easier than you think. Under the hood in LINQ the compiler generates a lot of code. It's possible that for a large generate SQL query where more than 64 fields are selected that you would hit this issue. Because the compiler under the hood would need to pass all of the fields to the constructor of an anonymous type.

Still a corner case.

JaredPar
+8  A: 

OK, I emerge from hiding... I used the following program to verify what was going on and while Marc pointed out that a string like this "{0}{1}{2}...{2147483647}" would succeed the memory limit of 2 GiB before the argument list, my findings did't match yours. Thus the hard limit, of the number of parameters you can put in a string.Format method call has to be 107713904.

int i = 0;
long sum = 0;
while (sum < int.MaxValue)
{
    var s = sizeof(char) * ("{" + i + "}").Length;
    sum += s; // pseudo append
    ++i;
}
Console.WriteLine(i);
Console.ReadLine();

Love the discussion people!

John Leidegren