1

The following test passes as written. Why? (i.e., why does a leading null value in an array cause string.Join() to return empty string?)

[TestMethod]
public void Test_string_Join()
{
    object[] values0 = { "foo", 3, null };
    var text0 = string.Join(", ", values0);
    Console.WriteLine("text0 = " + text0);
    AssertX.AreEqual("foo, 3, ", text0); // works as expected

    object[] values1 = { null, "foo", 3, null };
    var text1 = string.Join(", ", values1);
    Console.WriteLine("text1 = " + text1);
    AssertX.AreEqual("", text1); // does NOT work as expected, why empty?
}

enter image description here

dthal
  • 581
  • 1
  • 5
  • 16
  • Because NUL is the signal for end of string in NET and much of Windows – Ňɏssa Pøngjǣrdenlarp Apr 20 '18 at 02:08
  • [Relatd](https://stackoverflow.com/questions/16325995/string-join-method-that-ignores-empty-strings). – user202729 Apr 20 '18 at 02:09
  • @Plutonix [Nope](https://tio.run/##hY6xCsIwEIb3PsWRqcVaDI7FyU0UBAcHcUhjLJE0gVxalNJnj6lRcJB63HR8/38fxzlH7n2LUtdweKATTckVQ4S9NbVlDfTomJMcOiMvsGNSp@hsoE9nYLbGDPokgTCmugnuwrVjqhW4gBX0QK7GkByWOehWKRjKF9oxC07c3cjEsmJjQnEgw77zWWTXRqNRojha6cRWapGST5TALNYE9JcCjQqs4iT@z//40AkfOu1Dv3xGNBkG758). A `null` in the middle of the array doesn't return empty string. – user202729 Apr 20 '18 at 02:11
  • You might be able to find a null in the string - a char array - but most things will stop printing at the NUL – Ňɏssa Pøngjǣrdenlarp Apr 20 '18 at 02:11
  • @Plutonix Also, [a null does not prevent Console.WriteLine from printing](https://tio.run/##LczBCoJAEIDhu08x7EnJROgonbpFQdChQ3YY10021IWdUQrx2bdJG@Y0fPNr2mrSIQxk@wauH2LTFbpFIrh413jsYCJGthpGZ2s4o@1jYi/6/gD0DSUwRRHIuOplNMt1xHYwlMMeJlBP51QKuxQUVmWuawVzsfARPbB588@twezoJC5a9t9IVntwPbnWZDdv2Zxsb2Ilr2W@PCvYrCHB8xzCFw). This is not C. – user202729 Apr 20 '18 at 02:12
  • 7
    Because [the documentation says that](https://msdn.microsoft.com/en-us/library/dd988350(v=vs.110).aspx#Anchor_2). "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. [...] 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." – user202729 Apr 20 '18 at 02:15
  • @Plutonix The following works fine...so disproves the hypothesis (has a null and keeps printing). object[] values0b = { "foo", null, 3 }; var text0b = string.Join(", ", values0b); Console.WriteLine("text0b = " + text0b); AssertX.AreEqual("foo, , 3", text0b); // works as expected – dthal Apr 20 '18 at 02:15
  • 1
    @user202729 I was looking at the docs for String.Join(String, String[]). Wow!! String.Join(String, Object[]) and String.Join(String, String[]) are NOT symmetric in this regard!! – dthal Apr 20 '18 at 02:28
  • It does it because literally [that's what the code says](https://referencesource.microsoft.com/#mscorlib/system/string.cs,815fed0ee06a7bc8): `if (values.Length == 0 || values[0] == null) return String.Empty;` There is no comment in the source code either. Unless we can find the person who wrote it, the "why did some one choose this behavior?" aspect of this question is likely to go unanswered. – Mike Zboray Apr 20 '18 at 02:31
  • Microsoft uses the term "workaround". "A number of workarounds for this issue are available." And the behavior is not symmetric for Object[] and String[] (violates POLA). – dthal Apr 20 '18 at 02:34

1 Answers1

1

The question is about string.Join() for an "array". It turns out that Microsoft is violating the principle of least astonishment and String.Join(String, **Object[]**) behaves differently from String.Join(String, **String**[]) wrt a leading null. The object[] overload has the issue; the string overload behaves as one would expect.

I believe Microsoft is confessing as much when they refer to the "issue" and provide a "workaround" in the object[] overload documentation (link provided by @user202729).

I did not expect the docs for object[] overload to be different than the string[] overload (which I read thoroughly).

d219
  • 2,707
  • 5
  • 31
  • 36
dthal
  • 581
  • 1
  • 5
  • 16