3

What's going on here?

issue.CallAction has the following value:

"Blah blah - WebSite - 9/20/2017 - Containers remaining changed to 0."

Splitting on it, like so:

issue.CallAction.Split("Containers remaining changed to ").ToList()

returns more than 1 string element (as expected in this case) but the elements are:

  1. Blah blah - WebSite - 9/20/2017 -
  2. ontainers remaining changed to 0.

It was my understanding that Split replaces the entire string that you're splitting yet it's only replacing the first letter.

What gives?

Here's a screenshot of the compiler being happy with a string separator. Note the absence of the angry red squiggle:

Screenshot of the compiler happily allowing a string separator:

user4593252
  • 3,496
  • 6
  • 29
  • 55
  • 2
    I thought perhaps it was somehow passing the `string` as a `char[]` to the `String.Split(params char[] separator)` overload, but `"Blah blah - WebSite - 9/20/2017 - Containers remaining changed to 0.".Split("Containers remaining changed to ").ToList();` fails to compile with error `Argument 1: cannot convert from 'string' to 'char'` on .NET 4.7. In hindsight, `string` implements `IEnumerable`, but `.ToCharArray()` would be needed to make it a `char[]`. – Lance U. Matthews Sep 20 '17 at 20:30
  • Exactly my thought too because that's how it's behaving. I'm in .net 4.5.1 (I suppose I should tag that). I wonder if it's one of those things that they missed until 4.7? There's a big leap between 4.5 and 4.7. – user4593252 Sep 20 '17 at 20:32
  • I get the same compiler error if I build against .NET v4.5.1 on Visual Studio 2017 v15.3.5. – Lance U. Matthews Sep 20 '17 at 20:35
  • Wow, I get no such error. https://i.imgur.com/44By9jt.png – user4593252 Sep 20 '17 at 20:37
  • 2
    Aha! You're using VB.NET. I was compiling as C#. If I compile as VB.NET it does successfully compile, and I see the same results you do. IntelliSense shows that it is, in fact, calling the `params char[]` overload, which is why it's only splitting on the first character. – Lance U. Matthews Sep 20 '17 at 20:41
  • That's not very nice of it. It shouldn't be doing something other than what it's telling you it's doing. VB, why? Thanks – user4593252 Sep 20 '17 at 20:42
  • 2
    I believe this is a case where C# pushes you into the Pit of Success ([1](https://blog.codinghorror.com/falling-into-the-pit-of-success/), [2](https://i2.wp.com/csharpindepth.com/Files/PitOfSuccessAfter.png)). – Lance U. Matthews Sep 20 '17 at 20:52
  • 2
    Doh, looks like a duplicate of [VB.NET String.Split method?](https://stackoverflow.com/q/7919253/150605), anyways. Although, the output they said they got in that question is not consistent with A) this question, and B) my output when I run their code, so while the solution is the same perhaps the question _is_ different. – Lance U. Matthews Sep 20 '17 at 22:45

3 Answers3

4

To get compile error you can turn Option Strict On.

To split by string in VB.Net, you can use Microsoft.VisualBasic.Strings.Split:

Dim result = Split(issue.CallAction, "Containers remaining changed to ")
Slai
  • 22,144
  • 5
  • 45
  • 53
3

It looks like it's treating the separator as a Char. At least in Visual Studio 2017/.NET 4.5,C# which is what I'm using, as well as the current version there is no

string Split(string separator)

so you'd have to create an array of separators and populate the first one.

Try this:

        string s = "Blah blah - WebSite - 9/20/2017 - Containers remaining changed to 0.";

        string[] separator = { "Containers remaining changed to " };
        string[] splitted = s.Split(separator, StringSplitOptions.None);

EDIT:

I haven't used VB in several years, so I had to do some re-learning here. The output of this sample program tells me that VB quietly coerces a String into a Char when a Char is called for:

Sub PrintAChar(c As Char)
    Console.WriteLine("Printing your character: '" + c + "'")
End Sub

Sub Main()
    PrintAChar("B")                ' Prints 'B'
    PrintAChar("Bob Was Here.")    ' Also prints 'B'
End Sub

See @Heinzi's important comment below for a best practice.

Bob Kaufman
  • 12,864
  • 16
  • 78
  • 107
  • Huh, that... works. If you can figure out why there's no error, please let me know. It should be pitching a fit since the compiler seems fine with a string instead of an array. Thanks, btw. – user4593252 Sep 20 '17 at 20:22
  • 2
    Why there's no `Split( string separator )` bothers me to no end. Why it appears to be quietly converting your string to a char is a mystery for the moment. Stay tuned. Providing a bit more context may help, given these things behave differently in different situations. Is this in a `.cs` file or a code behind? Which version of .NET and which version of Visual Studio? – Bob Kaufman Sep 20 '17 at 20:25
  • .aspx.vb file, .net 4.5.1, VS 2013 – user4593252 Sep 20 '17 at 20:27
  • In case you'd like to see it, it's telling me that there is a string separator overload: https://i.imgur.com/44By9jt.png – user4593252 Sep 20 '17 at 20:38
  • 5
    Turn `Option Strict On` to disable this and other annoying implicit conversions. – Heinzi Sep 20 '17 at 20:40
  • 2
    @BobKaufman it's not converting it to a `char`. There's an overload for `Split` that takes an `IEnumerable`(which means split on any of these), and `String` implements `IEnumerable` – Richard Szalay Sep 20 '17 at 20:57
2

Isn't it interesting how the same code can appear to be both valid C# and VB.NET yet produce drastically different results? I did notice the lack of a trailing semicolon but assumed it was just a copy-and-paste artifact.

Using the following code...

Dim text As String = "Blah blah - WebSite - 9/20/2017 - Containers remaining changed to 0."
Dim list1 As List(Of String) = text.Split("Containers remaining changed to ").ToList()

...IntelliSense indicates that the String.Split(ParamArray() separator as Char()) overload is being called (the MSDN documentation includes the ParamArray modifier)...

VB.NET IntelliSense for String passed to String.Split method

Upon execution list1 then contains the following:

list1(0) = "Blah blah - WebSite - 9/20/2017 - "
list1(1) = "ontainers remaining changed to 0."

If you write it using this seemingly-equivalent syntax...

Dim list2 As List(Of String) = text.Split("C", "o", "n", "t", "a", "i", "n", "e", "r", "s", " ", "r", "e", "m", "a", "i", "n", "i", "n", "g", " ", "c", "h", "a", "n", "g", "e", "d", " ", "t", "o", " ").ToList()
Dim list3 As List(Of String) = text.Split("Containers remaining changed to ".ToCharArray()).ToList()

...IntelliSense indicates both .Split() calls are resolving to the same overload as above, yet the results for list2 are very different...

list2(0)  = "Bl"
list2(1)  = ""
list2(2)  = ""
list2(3)  = "bl"
list2(4)  = ""
list2(5)  = ""
list2(6)  = "-"
list2(7)  = "W"
list2(8)  = "bS"
list2(9)  = ""
list2(10) = ""
list2(11) = ""
list2(12) = "-"
list2(13) = "9/20/2017"
list2(14) = "-"
list2(15) = ""
list2(16) = ""
list2(17) = ""
list2(18) = ""
list2(19) = ""
list2(20) = ""
list2(21) = ""
list2(22) = ""
list2(23) = ""
list2(24) = ""
list2(25) = ""
list2(26) = ""
list2(27) = ""
list2(28) = ""
list2(29) = ""
list2(30) = ""
list2(31) = ""
list2(32) = ""
list2(33) = ""
list2(34) = ""
list2(35) = ""
list2(36) = ""
list2(37) = ""
list2(38) = ""
list2(39) = ""
list2(40) = ""
list2(41) = ""
list2(42) = ""
list2(43) = ""
list2(44) = ""
list2(45) = ""
list2(46) = ""
list2(47) = "0."

...and the same for list3. This is actually what we should have expected list1 to be all along since it would split on any one of the characters in the passed String.

Why is this happening? This is interesting to me, though I am not a VB.NET guy so perhaps this is common knowledge/pitfall. If we take a look at what the compiler produces we see this:

Dim text As String = "Blah blah - WebSite - 9/20/2017 - Containers remaining changed to 0."
Dim arg_13_0 As String = text
Dim expr_0E As Char() = New Char(0) {}
expr_0E(0) = "C"c
Dim list As List(Of String) = arg_13_0.Split(expr_0E).ToList(Of String)()
Dim arg_31_0 As String = text
Dim expr_26 As Char() = New Char(31) {}
RuntimeHelpers.InitializeArray(expr_26, fieldof(<PrivateImplementationDetails>.B46FB904AE74F7001A264BB77882D707B0E9ECC7).FieldHandle)
Dim list2 As List(Of String) = arg_31_0.Split(expr_26).ToList(Of String)()
Dim list3 As List(Of String) = text.Split("Containers remaining changed to ".ToCharArray()).ToList(Of String)()

That is, in the case of list2 it is passing a Char array consisting of all of the Chars in "Containers remaining changed to ", yet in the case of list1 it is passing a Char array consisting of only the first Char, "C"c! Talk about different results from similar-looking code...

I can't find a single document that connects all of these dots, but, basically, String will be implicitly converted to Char(), which calls the CChar conversion function, which returns only the first Char. Thus, the following four lines are equivalent:

Dim list1a As List(Of String) = text.Split("Containers remaining changed to ").ToList()
Dim list1b As List(Of String) = text.Split(CChar("Containers remaining changed to ")).ToList()
Dim list1c As List(Of String) = text.Split("C").ToList()
Dim list1d As List(Of String) = text.Split("C"c).ToList()
Lance U. Matthews
  • 15,725
  • 6
  • 48
  • 68
  • That's absolutely crazy that it would do that. Most of the time, C# and VB are equivalent enough that you don't need to know or care about this. Thanks for the details as well as the compiler code site. – user4593252 Sep 21 '17 at 13:21