6

I'm using string.Join() to concatenate values with delimiters - here is my simplified example using User class:

public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Here are my results:

User uItem = new User() { Age = 10, Name = null };

string item1 = string.Join(";", string.Empty, 1); // returns ";1"  
string item2 = string.Join(";", null, 1); // returns ""        
string item3 = string.Join(";", uItem.Name, uItem.Age, 1); // returns ""
string item4 = string.Join(";", 1, null); //returns "1;"
string item5 = string.Join(";", null, uItem.Age, 1); // throws null exception

First I was surprised that item2 doesn't behave like item1 and determines a "" instead of ";1".

Is there a rule like null is allowed but not at the first item?

My question: why does item3 return "" and item5 throws a exception? The input values are equal.

2 Answers 2

13

Because you are calling different overloads:

string item5 = string.Join(";", null, uItem.Age, 1); // throws null exception

will call

public static string Join(string separator, string[] value, int startIndex, int count);

(try doing a Go to Definition in Visual Studio and see). So you are passing null as the string[] value.

For the

string item2 = string.Join(";", null, 1); // returns ""

there is a remark in the String.Join Method (String, Object[]) page:

If the first element of values is null, the Join(String, Object[]) method does not concatenate the elements in values but instead returns String.Empty. A number of workarounds for this issue are available. The easiest is to assign a value of String.Empty to the first element of the array, as the following example shows

I do think that this (the special handling of the fist element) is quite stupid (even because the String.Concat Method (Object[]) doesn't have it)

Note that the other overload String.Join<T> Method (String, IEnumerable<T>) doesn't have this special handling:

string item2bis = string.Join(";", (IEnumerable<object>)new object[] { null, 1 }); // returns ";1"

If someone is interested, it isn't something random that causes it... There is explicit C# code in the String.Join implementation of Microsoft:

if (values.Length == 0 || values[0] == null)
    return String.Empty;
3
  • @fubo: because he passes ";", uItem.Name, uItem.Age, 1. The first parameter is the delimiter, the second one the null-string[], the third the startindex and the last the count. The params object[] overload would be taken if he would add another argument. Commented Mar 16, 2016 at 9:33
  • 1
    @fubo string.Join(";", null, uItem.Age, 1);; ";" is string, null is string[], uItem.Age is int, 1 is int
    – xanatos
    Commented Mar 16, 2016 at 9:34
  • 1
    Good answer xanatos, and a great demonstration of this 'bug-by-design'. Commented Mar 16, 2016 at 9:40
7

From the documentation (emphasis mine):

If separator is null or if any element of values other than the first element is null, an empty string (String.Empty) is used instead.

So the different handling of null is by design. Also, as noted by xanatos, using a different overload causes your exception, since it throws an ArgumentNullException if:

value is null

5
  • 2
    Doesn't explain the exception, this is one of the questions where it should be possible to accept two answers Commented Mar 16, 2016 at 9:26
  • 1
    This doesn't explain why item5 throws an exception. It simply says an empty string will be used instead of null. Commented Mar 16, 2016 at 9:26
  • @TimSchmelter Should be fixed now. Commented Mar 16, 2016 at 9:28
  • thanks for your reply i'd accept both answers if i could
    – fubo
    Commented Mar 16, 2016 at 9:38
  • No problem. You are welcome @fubo Xanatos did a great job. Commented Mar 16, 2016 at 9:38

Not the answer you're looking for? Browse other questions tagged or ask your own question.