views:

210

answers:

8
       'Why doesn't this work?
    Dim myStrings As String() = New String() {string1, string2, string3,}
    For Each s As String In myStrings
        If String.IsNullOrEmpty(s) Then
            s = ""
        End If
        s = "-" & s.Trim() & "-"
    Next

If string1 contains "foo", my intention is that string1 contains "-foo-" after the loop executes. How can I make this work?

I'm guessing that this code makes copies of my strings and modifies those. How can I modify my strings in a loop?

Update I've modified the code to use array indexes:

    ' It still doesn't work.
    Dim myStrings As String() = New String() {string1, string2, string3}
    For i As Integer = 0 To myStrings.Count() - 1
        If String.IsNullOrEmpty(myStringss(i)) Then
            myStringss(i) = ""
        End If
        myStrings(i) = "-" & myStrings(i) & "-"
    Next

The result of this code, referring specifically to the array and index for each element, modifies the array, but my strings still have the same old values. Apparently, initializing an array like this simply copies my values into a new array. How can I modify my original values in a loop?

Emphasis: The array is just for looping purposes. I need to modify string1, string2, and string3 similarly, but once this loop is thru, there is no more use for the array. Oh, and my real code has more than 3 strings.

Let me just say that if I were using a language with more pointers and references, I think I would simply have an array of pointers that point to string1, string2, andstring3`. It seems redundant to copy these strings into an array.

+2  A: 

This doesn't work because strings are immutable in .Net - you can't change the value of string1 without explicitly setting string1 to some value, i.e. by doing:

string1 = "Blah";

(Note that you can change the value of the ith string value in the myString array by (for example) using for instead of foreach, however from what I gather this isn't what you want to do)

To solve your problem you are going to need to given a better idea of what problem you are trying to solve, for example you could avoid the need to modify string references in this way by tweaking your interface (see this question for inspiration)

Kragen
@Henk sounds to me like they want to change the value of `string1`, not the contents of the array.
Kragen
Value semantics, not immutability.
Steven Sudit
+1  A: 

Iteration variables are immutable. Either use a "classic" loop or another array to store your strings.

DrunkenBeard
Again, not really.
Steven Sudit
Again ? If it's a reference to your comment about strings not being immutable, I'd agree that it's not the problem here. AFAIK Iteration variables (of any type) are read only, and that's the problem with the code above. Using a For/Next loop would solve it.
DrunkenBeard
@DrunkenBeard, I've posted some code using a For/Next loop that seems like it should solve the problem, but the result is that only the array is changed. `StringN` is still the old value. Can you suggest anything?
Rice Flour Cookies
@Henk http://msdn.microsoft.com/en-us/library/aa664754%28v=VS.71%29.aspx"A compile-time error occurs if the embedded statement attempts to modify the iteration variable"
DrunkenBeard
To clarify why this is about value semantics, not mutability, consider that if he were to pass in an array of mutable `struct` instances, anything he does to the values in the array would have no effect on the values used to initialize the array.
Steven Sudit
@Steven, yep I totally agree. After the edit it's clear that this isn't about mutability but value semantics. My answer was directed to Henk saying that iteration variables were not immutable.
DrunkenBeard
What the link says is that "the element variable is read-only", not immutable. So, for example, if we iterate through a list of `StringBuilder`, we could not say `s = new StringBuilder();`, but should be able to say `s.Append("stuff");`.
Steven Sudit
In fact, I just wrote a short program that confirms this.
Steven Sudit
@Steven, That's an interesting thought. What's the difference between an immutable object and a read-only one. I have searched but have found them to be used as synonyms pretty much everywhere. Still the StringBuilder example intrigues me.
DrunkenBeard
@DrunkenBeard No part of an immutable object should be changed once it is created. In .NET this is only a programming 'guideline', not something that is (completely) enforceable by VB.NET or C# at least. ReadOnly variables can only be set when the containing object is created, but their contents are mutable like any other object.
Mark Hurd
@DrunkenBeard: Mark's answer is correct. Mutability is a matter of class/struct design while `readonly` is a modifier you can slap on to a field to prevent it from being replaced (but not to prevent its potentially mutating methods from being called). When an object is immutable, making it `readonly` prevents any changes.
Steven Sudit
+1  A: 

It doesn't work because strings are immutable.

Try this instead.

For x As Integer = 0 To myStrings.Length - 1
   myStrings(x) = "new string"
Next

Edit after the update.

I'm not sure it's possible to do this.

Maybe you could just write a function?

string1 = MyFunction(string1)

Chris Diver
I don't think this has anything to do with the immutability of strings.
Steven Sudit
I'm going to rephrase: the issue isn't that strings are immutable, it's that they have value semantics. The `s` contains a perfectly good copy of the value, and all of the changes happen to the `s`. They just have no bearing on the corresponding element in `myStrings`.
Steven Sudit
@Henk: Yes, Chris' code does solve the problem. I'm sorry if I didn't make that clear.
Steven Sudit
@Steven, if `s` was a custom class and you do `s.Name = "Stephen"` then the item in the array is also modified as it is a reference type. The reason it doesn't work here is because you are effectively doing `s = New String("new string")`, so s is a new reference to a new string. You can't change s because it is immutable. At least that was my understanding.
Chris Diver
@Chris: This has more to do with value semantics than immutability. For example, if this were an array of `int`, each element is mutable, yet value semantics means that the value in the array is not linked to the value used to initialize the array.
Steven Sudit
Arrggh, value semantics! You would think that changing a set of strings would be such a simple tasks, but no matter how I group the strings, it copies the strings into the group so that only the copies are modified!
Rice Flour Cookies
@Steven, That's for trying to explain but I still don't understand it, i would have though the value in the array was linked to the value used to initialize it, until the point you try to modify it. I need to read some articles me thinks.
Chris Diver
@Chris: That would be reference semantics. With references, the object lives out on the heap, and the variables just refer to the object. That would mean that placing it in the array just creates a new reference to the same value, so changing the value through either reference would show up through the other. Value semantics means that each copy is independent, so changing one has no effect on any other. For example, an int is a value, so if I assign it from one variable to an other and then increment the first, the second is unchanged.
Steven Sudit
@Steven Okay, I understand that but why do value semantics apply to strings which are reference types? Don't strings live on the heap too?
Chris Diver
@Chris: Yes, but `string` is a strange case, where it is actually a reference but is designed to work almost exactly as if it were a value. Part of the trick is that it's immutable: you replace the string with a new one instead of actually changing the string itself. Another part is that the equality operators compare on content, not location. For reference objects, the default equality just tells us whether we're comparing something with itself.
Steven Sudit
@Steven - Thank you for explaining, I thought that by having value semantics you were implying that it was a value type. But it just means that it acts as a value type, I'd never though about `string` being anything other than it being immutable.
Chris Diver
@Chris: Right, it's not *literally* a value type as it doesn't inherit from `ValueType`, but it does bend over backwards to pretend it does. The reason I've peppered this page with "it's not a matter of immutability" is that this feature is only part of the equation for making `string` have the semantics of a value.
Steven Sudit
@Chris: The pass-through function you just added would work, although I'd consider making it take the string by reference, instead.
Steven Sudit
+5  A: 

This example is in C# but the same principle applies.

string[] myStrings = new string[] { "temp", "temp2", "temp3" };
myStrings = myStrings.Select(x => "-" + x + "-").ToArray();

Output:

"-temp-" "-temp2-" "-temp3-"

Link to the MSDN article: http://msdn.microsoft.com/en-us/library/bb548891.aspx

You can also use a delegate function to check whether the string is empty. Will work on providing a VB solution and edit when I get it

EDIT:

Dim myStrings As String() = _
        {"apple", "banana", "mango", Nothing, "passionfruit", "grape"}
    myStrings = _
        myStrings.Select(Function(fruit) ("-" & fruit & "-")).ToArray()

Output:

"-apple-" "-banana-" "-mango-" "--" "-passionfruit-" "-grape-"

EDIT 2: Does the same thing as the first edit but more readable

Private Function TESTER(ByVal fruit As String) As String
    Return "-" & fruit & "-"
End Function

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)              Handles MyBase.Load
    Dim myStrings As String() = _
       New String() {"apple", "banana", "mango", Nothing, "passionfruit", "grape"}
    myStrings = _
        myStrings.Select(AddressOf TESTER).ToArray()
End Sub
Gage
@Gage, what would this look like if `Function(fruit)` was several lines of code instead of just one line?
Rice Flour Cookies
@Rice Flour Cookies, that depends on what your trying to do. I'm used to using a delegate function but thats only in C#. I'll take a look at it. What are you trying to do with the strings?
Gage
+1  A: 

After your edit I don't think you can do this the way you want to. I think you're better off copying your strings to the array and working with the array. Instead of having string1, string2, etc ... you'd have array[0], array[1], etc ...

Is there anything preventing you from doing so ?

DrunkenBeard
I would prefer not to have to write another function to do this. I would like all the functionality in a single function. It looks like I can do this with "anonymous delegates" in .net 4.0
Rice Flour Cookies
+2  A: 

Given your most recent update, what you ask is not possible.

Strings follow value semantics, so anything you do to the string in the array has no effect on the original string. The closest you can come to a referenced string is StringBuilder, but it's not particularly easy to use in the way you want.

You need to take a step back and ask yourself why you're trying to do things this way; in other words, what your actual goal is. Once you share that, perhaps we can find a solution that works.

Steven Sudit
`string1`, `string2`, and `string3` are all passed as parameters to a function. I want to use them for different things within the function, but I first wish to apply a common operation to them.
Rice Flour Cookies
Ok, then write a `Shared` helper method that takes a `string` by reference and makes the changes. Then, at the top of your main method, simply call the helper once for each parameter. I know there's no loop, but then again, you were filling an array without a loop, too.
Steven Sudit
Per your response to DrunkenBeard, the helper method could easily be implemented as an anonymous delegate *without* a closure.
Steven Sudit
This answer appears to be correct, so why was it downvoted?
Steven Sudit
+2  A: 

Depending upon the nature of your collection, you could store unity-length arrays in it rather than strings. If you did that, you would then be able to change the 0th element of each array to update it.

For example, you could have a Dictionary(Of String, String()) which mapped strings onto string arrays. If single-item arrays were stored in the dictionary, you could easily alter the 0th item of any of them without disturbing the dictionary. Note that you should not do that with the dictionary keys, since the dictionary would not be able to search based upon the content of the strings.

BTW, I've sometimes wished for a collection class whose enumerator would return an object which could be used to request an alteration to the collection without jinxing the enumeration. Not sure how to manage such a thing without having to code the collection from scratch.

supercat
+1  A: 

This would be easily encapsulated into a ParamArray of ByRef parameters, but that isn't an option
:-(

So you seem to have to do it manually:

Imports System.Runtime.CompilerServices

Module Module1

Sub Main()
    Dim String1 = "foo"
    Dim String2 = ""
    Dim String3 As String = Nothing
    Dim strings() = New StrongBox(Of String)() {New StrongBox(Of String)(String1), New StrongBox(Of String)(String2), New StrongBox(Of String)(String3)}
    Modify(strings)
    String1 = strings(0).Value
    String2 = strings(1).Value
    String3 = strings(2).Value
    Console.WriteLine("'{0}' '{1}' '{2}'", String1, String2, If(String3, "NOTHING"))
    Console.ReadLine()
End Sub

Sub Modify(ByVal ParamArray myStrings() As StrongBox(Of String))
    For i As Integer = 0 To myStrings.Count() - 1
        If String.IsNullOrEmpty(myStrings(i).Value) Then
            myStrings(i).Value = ""
        End If
        myStrings(i).Value = "-" & myStrings(i).Value & "-"
    Next
End Sub

End Module

EDIT: Note that it is simpler to do something like this (it just doesn't supply anything like what the question is asking for):

Module Module1

Sub Main()
    Dim String1 = "foo"
    Dim String2 = ""
    Dim String3 As String = Nothing
    Modify(String1)
    Modify(String2)
    Modify(String3)
    Console.WriteLine("'{0}' '{1}' '{2}'", String1, String2, If(String3, "NOTHING"))
    Console.ReadLine()
End Sub

Sub Modify(ByRef myString As String)
    If String.IsNullOrEmpty(myString) Then
        myString = ""
    End If
    myString = "-" & myString & "-"
End Sub

End Module
Mark Hurd