Passing a Reference to a String in C#

In summary: I figured I'd ask first.In summary, the issue is that the tooltip text from ClassA is being passed into ClassB's GenerateTooltip method, but the code inside ClassB isn't using the updated text, which causes the original text to be shown.
  • #1
Drakkith
Mentor
23,094
7,504
TL;DR Summary
Trying to update the text for a tooltip each frame.
I'm using C# to program in Unity and I'm having some trouble with getting a tooltip box to update every frame.
Simplified, Class A (and several other classes and instances of classes) has a string that stores the tooltip text and updates it every frame.
Class B contains the logic to generate the tooltip box and everything inside it.
Class A calls a method in Class B with a GenerateTooltip method linked to an OnMouseOver event.
The issue is that even though the tooltip text is being updated every frame, Class B doesn't appear to have access to this updated information and only shows the original text passed through the function. Simplified code:

C#:
public Class ClassA{
    string tooltipText;
    
    void Update(){ //Called every frame by Unity.
        UpdateTooltipText(); //Updates tooltipText variable with various things that can change each frame.
    }    
    void GenerateTooltip(){ //Called with a mouseover event.
        ClassB.CreateTooltip(tooltipText) //Passes tooltip text to Class B.
    }
}

public Class ClassB{    
    public CreateTooltip(string passedTooltipText){
        tooltipBox.text = passedTooltipText; //Updates the tooltip UI label with the correct text to display.
        //Logic to generate tooltip of the right size and position.
    }

void Update(){ //Called each frame.
        if (tooltipVisible){
             tooltipBox.text = tooltipText; //Updates the tooltip UI box each frame, but tooltipText 
                                           //is not updating based on what Class A is doing to its tooltipText variable.
        }
    }
}

However, this doesn't seem to work. My understanding was that strings are reference types and thus Class B would have access to the updated tooltipText variable from Class A each frame. But apparently I am incorrect and reference types don't work like this. Can someone explain why this doesn't work and what I could do to fix it?
 
Technology news on Phys.org
  • #2
I'm not really familiar with C#, however...

Looks like Update () is defined twice, once in line 4, then again in line 18.
 
  • #3
Tom.G said:
I'm not really familiar with C#, however...

Looks like Update () is defined twice, once in line 4, then again in line 18.
They are supposed to both have an update method. Both classes are actually inheriting the Monobehavior class from Unity, which has an Update() method. The children methods are automatically called each frame for every existing object that's inherited it without having to do any extra coding or use any other syntax. Unity even auto-defines it when you make a new script (c# file) through Unity's editor interface.

Edit: I've only been using C# for about 2 months, but I think having the same method in two different classes is fine even if they don't inherit it. Aren't these sort of like the local variables inside a method that can have the same name as other local variables inside other methods?
 
Last edited:
  • Informative
Likes Tom.G
  • #4
I am not aware how Unity work in this regards, but I seem to recall some UI-framework (years ago) that could not update the tooltip that was already showing, or at least required special coding for that to happen. That is, the tooltip had to timeout (or the mouse had to be moved out of the component) for the text to show updated text on next activation of it. If the tooltips in your case only updates "between" activation, maybe Unity has this quirk too, and perhaps you then need to update both the string you want to display and, when it is already showing, update the tooltip rendering component too.
 
  • #5
Filip Larsen said:
I am not aware how Unity work in this regards, but I seem to recall some UI-framework (years ago) that could not update the tooltip that was already showing, or at least required special coding for that to happen.
Nope, I've tested it and I can update the tooltip from within ClassB's Update method with a simple integer increment statement. The tooltip is just a Label from Unity's UI toolkit like the ones I already use elsewhere. These others update each frame just fine since I'm calling the functions locally and pulling data from elsewhere. In this case I'm calling the function from outside the class and passing data in.

I'm pretty sure I can get it to work with a spaghetti-coded workaround (I've already got an idea or two that I haven't yet implemented), but I thought that passing a string variable as an argument in a method call passed the reference to the string, not just a copy. But there's something more limited about it that I'm not aware of apparently. So my primary goal is to understand more about passing reference types around, with the secondary goal of figuring out how to make my tooltip update.
 
  • #6
Drakkith said:
Edit: I've only been using C# for about 2 months
I think this is the problem: I assume this is the first time you have used an object oriented language? You seem to be confused betwen classes and objects.

What you have posted above is obviosly edited down from the complete code, and because of this confusion your code just doesn't make sense. For instance the following code
C#:
        // Adding comments like the one on the next line is NOT helpful
        // and makes it harder for others to help you: don't do it.
        ClassB.CreateTooltip(tooltipText) //Passes tooltip text to Class B.
calls a static method defined in the class definition for ClassB, but in ClassB the method CreateTooltip is not defined as static. And in ClassB.Update you have the following code:

C#:
            // See how difficult it is to read the comment relating to the next
            // statement...

            tooltipBox.text = tooltipText; //Updates the tooltip UI box each frame, but tooltipText
                                           //is not updating based on what Class A is doing to its tooltipText variable.

            // ...compared to the following

             // Update the tooltip UI box each frame.
             // FIXME: tooltipText is not updating based on what ClassA is doing
             // to its tooltipText property.
             tooltipBox.text = tooltipText;
Why do you think that tooltipText in a member function of ClassB refers to something declared in ClassA?
 
  • Like
Likes jim mcnamara
  • #7
pbuk said:
I think this is the problem: I assume this is the first time you have used an object oriented language? You seem to be confused betwen classes and objects.
Yes, this is my first object oriented language.
pbuk said:
calls a static method defined in the class definition for ClassB, but in ClassB the method CreateTooltip is not defined as static.
Sorry, what about the code in ClassA makes the method call a static method call?
pbuk said:
Why do you think that tooltipText in a member function of ClassB refers to something declared in ClassA?
The book I'm using to learn C# states that classes are reference type variables, and since the 'string' keyword is just an alias for the 'system.string' class, it should be a reference type. Reference type variables pass a reference to themselves during assignments and into methods, instead of a copy like value type variables do. So for example:

C#:
class ExampleClass{
    
    string testString;
    string otherString;
    
    void Main(){
        testString = "Apple";
        otherString = testString;
        otherString = "Orange" //Should assign "Orange" to testString??
        Console.Writeline(testString);
        ExampleMethod(otherString);       
    }
    void ExampleMethod(string passedString){
        passedString = "Pear" //Should assign "Pear" to testString??
        Console.Writeline(testString);
    }
}
This code, according to my understanding based on what I've read from my book, should display, "Orange" and then, "Pear". However, testing I performed just now shows this is not the case. Instead it displays "Apple" and "Apple".

However, consider this code:
C#:
class TestClass{
    WrappedInt wi; //Defined below.
    
    void Main{
        wi = new WrappedInt();
        ChangeWrappedInt(wi);
    }
    void ChangeWrappedInt(WrappedInt arg){
        arg.number = 10;
        Console.Writeline(wi.number); //Writes out wi.number, not arg.number
    }
}

public class WrappedInt{
    public int number;
    public WrappedInt(){
        number = 5;
    }
}
This code displays "10", just how the book explains. So I guess strings are reference types, but are somehow different than other classes?
 
  • #8
Drakkith said:
what about the code in ClassA makes the method call a static method call?
The function call to ClassB.createTooltip. This doesn't call the method on an instance of ClassB--in fact, in the code that you've shown there are no instances of classes anywhere (which is extremely weird). It calls the method on the class itself.

Drakkith said:
this is my first object oriented language.
Then I would strongly suggest taking some time to learn the general principles of object oriented languages, things like the difference between classes and instances of classes.
 
  • #9
This video explains and illustrates all the needed OO concepts. The examples are in Python, but the concepts apply to all OO languages.

 
  • #10
Drakkith said:
This code displays "10", just how the book explains.
How is this code run? All I see is class and function definitions; there is no actual code that runs anything.

That said, in your Main() function, there is a line that creates an instance of WrappedInt (the new command). Then your ChangeWrappedInt function takes an instance of WrappedInt as an argument and does something to it. But there is no similar code in what you posted before: no instance of either ClassA or ClassB is created, and the function that appears to be intended to do something to an instance of ClassB does not take an instance of ClassB as an argument.
 
  • #11
PeterDonis said:
How is this code run? All I see is class and function definitions; there is no actual code that runs anything.
My apologies. It's a side effect of using C# exclusively in Unity and not standalone. Unity automatically instantiates certain scripts (scripts are C# files, so I guess it's better to say Unity instantiates certain classes in certain scripts), so you don't always have to explicitly do so in your code. I'm not 100% sure of the details, but trust me, the code is being run.

PeterDonis said:
Then I would strongly suggest taking some time to learn the general principles of object oriented language, things like the difference between classes and instances of classes.
I'm aware of the difference. I just haven't dealt with static methods yet, and combined with Unity automatically creating instances of scripts I didn't immediately understand why such a method call looked like a static method call.

Again, my apologies on the confusion.
 
  • #12
Drakkith said:
Unity automatically instantiates certain scripts
Loading and executing code automatically is one thing. Automatically instantiating classes is another. Is Unity creating instances of ClassA and ClassB automatically?

Even if Unity is automagically instantiating ClassA and ClassB somewhere, your code still gives no way for your GenerateTooltip function to get a reference to whatever instance of ClassB Unity is creating. If Unity is creating class instances behind the scenes, the Unity documentation should be telling you how it expects to give references to those instances to your code.
 
  • #13
PeterDonis said:
Loading and executing code automatically is one thing. Automatically instantiating classes is another. Is Unity creating instances of ClassA and ClassB automatically?
It is. You quoted my post before my edit went through. See above.
PeterDonis said:
Even if Unity is automagically instantiating ClassA and ClassB somewhere, your code still gives no way for your GenerateTooltip function to get a reference to whatever instance of ClassB Unity is creating.
Sorry, I guess in my attempt to simplify the code for easier reading I assumed no one would worry about references and such. In my game, everything has a reference to a ReferenceHolder class, which itself has references for all other classes. So they all have references to each other through this other class.
 
  • #14
Drakkith said:
In my game, everything has a reference to a ReferenceHolder class, which itself has references for all other classes. So they all have references to each other through this other class.
Ok, then I would expect to see a reference to an instance of ClassB through a ReferenceHolder class in the code you posted in the OP. But I don't see that.
 
  • #15
PeterDonis said:
Ok, then I would expect to see a reference to an instance of ClassB through a ReferenceHolder class in the code you posted in the OP. But I don't see that.
Well, I never dreamed that my simplified code example would be scrutinized to this extent. All I really wanted to know was why passing a string didn't work like I thought it did.
 
  • #16
Drakkith said:
All I really wanted to know was why passing a string didn't work like I thought it did.
And my point is that we cannot help you with that if we can't make sense of the code you show us. I can't make sense of the code you show us in the OP because I can't tell where a reference to an instance of ClassB, which seems to me to be required to do what you are trying to do, is coming from.

Btw, I also can't make sense of what the tooltipBox variable in the ClassB CreateTooltip method refers to. It is supposed to be a class member variable? How does it get initialized?

Btw btw, I also can't make sense of where the tooltipText variable in the Class B Update method is coming from. That variable seems to be a member variable of ClassA, not ClassB, and no value or reference to such a variable is passed to the method.
 
  • #17
Drakkith said:
why passing a string didn't work like I thought it did.
Btw, given the issues I see with the code you posted, I think it's quite possible that you are misdiagnosing the problem. The actual problem might have nothing to do with how strings work; it might be that your code is not correctly passing around references to objects in general.
 
  • #18
PeterDonis said:
Btw, I also can't make sense of what the tooltipBox variable in the ClassB CreateTooltip method refers to. It is supposed to be a class member variable? How does it get initialized?
I left it out because I didn't think it was important to include it. That's why I put comments explaining what it was doing.
PeterDonis said:
Btw btw, I also can't make sense of where the tooltipText variable in the Class B Update method is coming from. That variable seems to be a member variable of ClassA, not ClassB, and no value or reference to such a variable is passed to the method.
This was just an honest mistake. I absolutely meant to include this variable declaration in ClassB.
 
  • #19
Drakkith said:
I left it out because I didn't think it was important to include it.
For myself, at least, I don't think this is a good idea. Whenever you post code asking for help with it, it should be a self-contained, consistent block of code that includes everything necessary to make sense of what it is doing. (You'll notice that often, in threads discussing Python, I post actual transcripts of Python interactive interpreter sessions, showing every step of what I am doing explicitly.)

Drakkith said:
I absolutely meant to include this variable declaration in ClassB.
Fair enough. This still doesn't address the main points, though, which are:

How does the GenerateTooltip method of ClassA get a reference to the instance of ClassB that it needs?

How does the Update method of ClassB get a reference to the tooltipText string that it needs?
 
  • Like
Likes Mark44
  • #20
PeterDonis said:
Btw, given the issues I see with the code you posted, I think it's quite possible that you are misdiagnosing the problem. The actual problem might have nothing to do with how strings work; it might be that your code is not correctly passing around references to objects in general.
I agree with this diagnosis, but I also think you have a problem with:

Drakkith said:
All I really wanted to know was why passing a string didn't work like I thought it did.
But before we fix this, let's clear up this question:

Drakkith said:
Sorry, what about the code in ClassA makes the method call a static method call?
This is because you call
C#:
ClassB.createTooltip()
An instance method call would look like this:
C#:
// Create an instance of ClassB (this could already exist). 
ClassB bThing = ClassB();
bThing.createTooltip();

Now let's rewrite your code so it works, adding some comments (you can see it running at https://replit.com/@pbuk/HeartfeltGoodAlgorithms#main.cs).
C#:
using System;

class ExampleProgram
{

    // Because we are accessing these properties from the static method Main
    // they must be defined as static.
    static string testString;
    static string otherString;

    // Main is always a static method.
    static void Main()
    {
        // Create a new string object with the value "Apple" and assign a
        // reference to it to the static property ExampleProgram.testString.
        testString = "Apple";
        // Copy the reference to the object held in ExampleProgram.testString to
        // the static property ExampleProgram.otherString.
        otherString = testString;
        // Create a new string object with the value "Orange" and assign a
        // reference to it to the static property ExampleProgram.otherString.
        otherString = "Orange";
        // testString has not been changed, it is still "Apple".
        Console.WriteLine(testString);
        ExampleMethod(otherString);
        Console.WriteLine(otherString);
    }

    // Because we are accessing this method from the static method Main it must
    // be defined as static.
    static void ExampleMethod(string passedString)
    {
        // Create a new string object with the value "Pear" and assign a
        // reference to it to the local variable passedString.
        passedString = "Pear";
        // testString has not been changed, it is still "Apple".
        Console.WriteLine(testString);
    }
}
 
  • #21
Drakkith said:
This code, according to my understanding based on what I've read from my book, should display, "Orange" and then, "Pear"
No, it shouldn't, because the variable passedString is passed by value. It starts out with whatever string value is passed to it, which happens to be the string value that is referenced by the class member variable testString, but changing it does not change testString, it only changes the local variable passedString inside the method.
 
  • #22
PeterDonis said:
No, it shouldn't, because the variable passedString is passed by value.
Why is it passed by value if a string is a reference type? Is this unique to the string class?
 
  • #23
Hey! Got it to work! Had to create a new StringObject class to do it, but it works! StringObject class just contains a single string variable and a getter and setter. Changed my code to use this new StringObject instead of a string and it worked immediately!
 
  • Like
Likes harborsparrow
  • #24
PeterDonis said:
For myself, at least, I don't think this is a good idea. Whenever you post code asking for help with it, it should be a self-contained, consistent block of code that includes everything necessary to make sense of what it is doing. (You'll notice that often, in threads discussing Python, I post actual transcripts of Python interactive interpreter sessions, showing every step of what I am doing explicitly.)
Noted. I'll keep this in mind for the future. Thanks, Peter.
 
  • #25
Drakkith said:
Why is it passed by value if a string is a reference type?
These are two different, unrelated things. A string in C# is a "reference type" in the sense that, "under the hood", so to speak, it is a pointer to a structure, not just a memory location that stores the data directly. By contrast, an integer in C# is a "value type", because it is just a memory location that stores the data (the bits that code for the integer) directly.

However, regardless of whether a particular variable is a value type or a reference type, all function parameters in C# are passed by value by default. See, for example, here:

https://www.c-sharpcorner.com/UploadFile/puranindia/parameter-passing-in-C-Sharp/
 
  • Like
Likes harborsparrow
  • #26
PeterDonis said:
However, regardless of whether a particular variable is a value type or a reference type, all function parameters in C# are passed by value by default.
That's true in the sense that whatever is in the argument is copied to the parameter. But for reference types that copy is a copy of the address of the class instance, allowing you to make changes to that instance from inside the method and have them be seen outside of the method. See here: https://learn.microsoft.com/en-us/d...hod-parameters#pass-a-reference-type-by-value

Since strings are classes, I expected to have them behave as reference types. But they either do not, or I was somehow using them incorrectly.
 
  • #27
From your link
Drakkith said:
When you pass a reference type by value:
  1. If the method assigns the parameter to refer to a different object, those changes aren't visible from the caller.
  2. If the method modifies the state of the object referred to by the parameter, those changes are visible from the caller.

When you write myStringParameter = "text" you are doing 1, when you write myObjectParameter.stringProperty = "text" you are doing 2.
 
  • Like
Likes Nugatory
  • #28
Drakkith said:
Since strings are classes, I expected to have them behave as reference types.
Once again, "reference types" and "passing function parameters by reference" are two different, unrelated things, and you should not expect them to always go together. This is not something that is peculiar to C#. In Python, for example, all objects are "reference types"; under the hood, they are all pointers to structures. But also in Python, the only way to pass a function parameter is by value, because "variables" in Python are not memory locations or pointers, they are namespace bindings, and "setting the value of a variable" does not mean mutating an object pointed to by the variable, but changing the namespace binding so that variable name points to a new object, and a function does not have access to the namespace of its caller.
 
  • #29
PeterDonis said:
Once again, "reference types" and "passing function parameters by reference" are two different, unrelated things, and you should not expect them to go together.
I'm not asking how to pass something by reference. Not in the strict sense of using ref or out or anything like that. I was confused as to why strings weren't behaving like reference types as they were explained by my book.

pbuk said:
When you write myStringParameter = "text" you are doing 1, when you write myObjectParameter.stringProperty = "text" you are doing 2.
I guess that makes sense. Now that I think more about it, typing stringVariable = "some string"; creates a new instance of a string class, right?
 
Last edited:
  • #30
Drakkith said:
I guess that makes sense. Now that I think more about it, typing stringVariable = "some string"; creates a new instance of a string class, right?
Yes that's right.

It's probably also worth mentioning that strings in C# are immutable: it is not possible to change their state. If you want to pass something to a function that modifies it then you can use the StringBuilder class.
 
  • Like
Likes rbelli1
  • #31
pbuk said:
Yes that's right.

It's probably also worth mentioning that strings in C# are immutable: it is not possible to change their state. If you want to pass something to a function that modifies it then you can use the StringBuilder class.
Thanks, pbuk. I got around whatever limitation kept me from passing the reference to the original string around by creating my own class.
 
  • #32
pbuk said:
strings in C# are immutable
Drakkith said:
limitation kept me from passing the reference to the original string

When you tried to change the "original string" somewhere else you didn't actually change it. you created a new string then assigned it to the "other" variable. The "original" happily hung out and was used for the tooltip.

BoB
 
  • Like
Likes harborsparrow and Drakkith
  • #33
rbelli1 said:
When you tried to change the "original string" somewhere else you didn't actually change it. you created a new string then assigned it to the "other" variable. The "original" happily hung out and was used for the tooltip.

BoB
Well that answers that. Thank you!
 
  • #34
Oh I'm an idiot. From my own link in post #26:

Because a string "modification" is actually a new string creation, you must use caution when you create references to strings. If you create a reference to a string, and then "modify" the original string, the reference will continue to point to the original object instead of the new object that was created when the string was modified.

The explanation was right in front of me as soon as I found that page and I just missed it completely.
 
  • #35
Drakkith said:
The explanation was right in front of me as soon as I found that page and I just missed it completely.
Sounds familiar... <wry smile emoji>
 

FAQ: Passing a Reference to a String in C#

What is the difference between passing a reference to a string and passing the string itself in C#?

When passing a reference to a string, the actual string data is not copied, but rather a reference to the memory location where the string is stored is passed. This means that any changes made to the string inside the method will also affect the original string. When passing the string itself, a copy of the string data is made and passed to the method, so any changes made inside the method will not affect the original string.

How do I pass a reference to a string as a method parameter in C#?

To pass a reference to a string as a method parameter in C#, you can use the ref keyword before the string parameter in the method signature. This will allow any changes made to the string inside the method to be reflected in the original string.

Can I pass a reference to a string as an out parameter in C#?

Yes, you can pass a reference to a string as an out parameter in C#. This allows the method to assign a new string value to the parameter, which will be reflected in the original string when the method is finished.

Is passing a reference to a string more efficient than passing the string itself in C#?

Passing a reference to a string is generally more efficient than passing the string itself in C#. This is because passing a reference only requires the memory location of the string to be passed, while passing the string itself requires a copy of the string data to be made and passed.

Can I pass a reference to a string as a parameter to a method that expects a string in C#?

Yes, you can pass a reference to a string as a parameter to a method that expects a string in C#. This is because a reference to a string can be implicitly converted to a string in C#. However, it is important to note that any changes made to the string inside the method will also affect the original string.

Similar threads

Replies
6
Views
3K
Replies
7
Views
3K
Replies
12
Views
2K
Replies
2
Views
3K
Replies
4
Views
2K
Replies
11
Views
3K
Replies
9
Views
3K
Back
Top