As a summary: I'm trying to get String.Concat to use a reference type's ToString overload when sticking string together.
Edit: Added this overview: The example code below is a cooked down extract of my real code - as such it would be immediately obvious when refactoring if I only had two lines of code. The important issue here (to me) is that I changed from a string to an object and there was no compile error from String.Concat. Equally it's behaviour wasn't what I would have expected (Using my object's to string method, rather than the bog standard object name). If I'd been using "&", there would have been a compile error. I'm concerned that the String.Concat syntactic sugar may lead to bugs that otherwise would have been avoided (in this case when refactoring). I'd like to know if there's a way of altering the behaviour of String.Concat or if I should consider it to be dangerous.
The situation:
I've got a solution which processes a whole heap of data; I was using a String to contain the identifier of each piece of data, but have just swapped this out for a class (FeatureIdentifier) to enable me to extend the identifier to include things like batches etc.
I've refactored my code so that I use this class instead of just the string. When refactoring this type of thing (rightly or wrongly) I tend to rely on compile errors as a to-do list.
Now, I'm a self-taught programmer and I'm probably a bit set in my ways (I tend to look at new features in terms of if they let me do anything new rather than if they let me do stuff I can already do only easier) and I've just come across something which makes me sad.
So, I was sticking my identifier onto an underscore onto a type. My code looked like this:
Dim x as string = "MyIdentifer"
dim myOutputValue as string = String.Concat(x,"_ANCHOR.txt")
Running this I got myOutputValue equal to "MyIdentifier_ANCHOR.txt". Following refactoring, my code looked like this:
Dim x as new FeatureIdentifier("MyIdentifier")
dim myOutputValue as string = String.Concat(x,"_ANCHOR.txt")
Running this I got myOutputValue equal to "MyNamespace.FeatureIdentifier_ANCHOR.txt".
Having kicked myself and implemented a ToString method on my class, I run it again and get exactly the same output (that is "MyNamespace.FeatureIdentifier_ANCHOR.txt"). In immediate, if I do: ?x.ToString, I get "MyIdentifier", so I'm certain I've implemented ToString correctly.
So, here's my problem. I like the syntax of String.Concat but I don't like the fact that it doesn't do one of:
a) calling ToString on reference types it sticks together or
b) throwing a compile error if you pass it non-string based arguments. The old school: x & "_ANCHOR.txt" gives me a compile error (which I would have picked up when refactoring).
Here's what I've tried:
I've tried shadowing the String.Concat function with an extension (something like this:
<System.Runtime.CompilerServices.Extension()> Public Function Concat(...some arguments...) As String
Return String.Concat(...some arguments...)
End Function
) but hit two problems:
1) When trying to narrow the type of the arguments down to string to cause compile errors, I realised that the argument is a param array and hence objects in the first place. So fail there.
2) When I tried to make multiple overloads ((s1 as string, s2 as string), (s1 as string, s2 as string, s3 as string) etc), I felt it was a bit lame and also discovered that you can't actually overload an extension on a static class (which is what I guess String is).
So, does anyone know a way of getting String.Concat to behave as well as old-school concatenation, or should I avoid String.Concat in favour of old-school concatenation?.
(I'm not going to use a StringBuilder, as I'm only concatenating a few strings and I don't believe this is the place for one).
2 Answers
I don't believe there is any way that you are going to "fix" the String.Concat method to only allow strings. Chalk it up to another reason why shared methods should be created and used as sparingly as possible. However, through the miracle of operator overloading, you can make your custom class work just like a string. To fix the Concat method, you need to overload the CType operator. To fix the string concatenation operator (&), you need to overload that operator separately, like this:
Public Class FeatureIdentifier
Public Sub New(id As String)
Me.Id = id
End Sub
Public Property Id As String
Public Property SomethingElse As Integer
Public Overloads Shared Widening Operator CType(value As FeatureIdentifier) As String
Return value.Id
End Operator
Public Overloads Shared Operator &(value1 As FeatureIdentifier, value2 As String) As String
Return value1.Id & value2
End Operator
Public Overloads Shared Operator &(value1 As String, value2 As FeatureIdentifier) As String
Return value1 & value2.Id
End Operator
End Class
Now you can use it like this:
Dim x As New FeatureIdentifier("MyIdentifier")
Dim myOutputValue As String = String.Concat(x, "_ANCHOR.TXT")
Or like this:
Dim x As New FeatureIdentifier("MyIdentifier")
Dim myOutputValue As String = x & "_ANCHOR.TXT"
And it will work just like as if it were still a string, in those circumstances. You may also want to overload some of the other operators too, just in case. For instance, the + operator also concatenates when applied to two strings. However, I should caution you that operator overloading can cause confusion to people who are not familiar with the code, since it works unexpectedly, so you should only use it if it really makes sense to do so.
3 Comments
Do you need to override ToString and even make sure it's called for this? It would seem to be more proper to expose a property of FeatureIdentifier named Identifier, or some such thing, and then you can just do:
Dim myOutputValue as string = String.Concat(x.Identifier, "_ANCHOR.txt")
3 Comments
ToString on an object of type string will just be the string, not the full type name.