how to swap classes?

right now im trying to swap classes from a field to another

    MyClass temp = a;
        a = b;
        b = temp;

if both a and b are instances of myClass

the issue is that when you do a = b, since temp is = a then now all classes are b

any advice on how this works would be appreciated

No. t does not magically become the original b. t still references the original a. The code is correct.
You can also make it shorter.

(a, b) = (b, a);
3 Likes

Also, you’re not swapping classes, you’re swapping references to instances of a class.

6 Likes

Im trying to figure out where exactly am I losing the references to instances of my class

my code is kind of long but its confusing because, some classes arrive at the end of the code as the original instance

while others I guess got passed as returned fields

MyClass a;

MyClass b;

class MyContainer{

MyClass ref;

}

public MyContainer c = new MyContainer()

public MyContainer placeClassInsideContainer( MyClass _e){

c.ref = null;
c.ref = _e;

return c;

}

and then when I do

MyClass a = new MyClass();

MyClass b= new MyClass();

MyContainer c = new MyContainer()


c = placeClassInsideContainer( a);

Debug.Log( c.ref == a );//is this supposed to come out true or false?

I dont understand the rules of when a new instance of the class was created, or where does it remain a reference to the class

Your examples are really confusing and broken. They confirm you don’t understand the rules of variable scope and lifetime, which is how computer scientists would call it. You make it more muddy by naming ‘a’, ‘b’, ‘c’ and ‘ref’ and then accessing them and returning them in unconventional ways, due to your misunderstanding.

https://www.ict.ru.ac.za/resources/thinksharply/thinksharply/scope_and_lifetime.html

class MyContainer
{
    public MyThing reference;

    public void PutThingInContainer(MyThing d)
    {
        reference = d;
    }
}

class SomeOtherPlace
{
    public MyThing a = new MyThing();
    public MyThing b = new MyThing();
    public MyContainer c;
    void SomeOtherMethod()
    {
        c = new MyContainer();
        c.PutThingInContainer(a);
        Assert.IsTrue( c.reference == a );
    }
}

The scope of ‘a’ is any code in SomeOtherPlace. The lifetime of the MyThing instance referred by ‘a’ is potentially the entire lifetime of any instance of SomeOtherPlace, but also potentially longer, if you copy either of those references ‘a’ or ‘c’ outside of the SomeOtherPlace instance.

3 Likes

basically what i am having a ton of trouble understanding is where a class instance is exactly located

so if I ever do myClass a = new myClass()

then “a” doesnt contain the class, only a reference to the class, and the class doesnt exist in any field, only in memory, and it will only stop existing when there are no more references to it and it gets garbage collected

is this much correct, or is it already wrong

the further confusion is that I believed I could change the reference of a field, by using “=” on a field that has the same reference for example:

myClass a = new myClass(1);

myClass b = a

myClass c = new myClass(2);

b = c

//it was my belief that now since "b" points to the same place as "a", if I change b with a "=" operand it will also change "a"

thus i believed that in the end I would have “a == c” which apparently is incorrect, right?

Yes that would be incorrect. Assigning a reference doesn’t change what other references are pointing to. That would make reference types pretty much useless…

Mutating what is at the end of said reference to does of course reflect across everything else pointing to it, naturally, because they’re all looking at the same thing.

Just think about how referencing scriptable objects works. You have three things referencing the same scriptable object via the inspector, and reading a value out of it. You change that value, all three reference are seeing the new value. This is pretty much how normal C# references work, because, well, they’re more or less exactly the same thing in the end of the day.

so lets say I have this scenario

class myClass{
public int myInt;
}

myClass a = new myClass();
a.myInt = 236;
myClass b = a
myClass c = new myClass();
c.myInt = 67;

and now I want to do “a=c”,

BUT, lets say that I have no access to “a”, only “b” and “c”.

Is there a way to change what “a” is pointing to without actually expressing a call for a?

I imagine that it would work if I took “b”, and I simply did b.myInt = c.myInt, but for a more complex class this would be quite bothersome to just iterate through the whole class composition

Or is it just a design flaw by me, for not having direct access to “a”? Should I just design the code such that access to “a” is always available?

How about you give us an actual concrete use-case you’re trying to solve here rather than these completely arbitrary and annoyingly abstract a, b and c examples?

This feels like an x/y problem.

1 Like

its just a core knowledge issue, I just dont know these basic principles.

I dont want a solution to a problem, I just want to understand how it works and what the language is capable of, or what is the standard way to handle these situations

the naming is abstract on purpose because the question is abstract

I’m not searching for workarounds, just trying to learn the way things should be done

The reason why you’re getting the replies you are is because this forum isn’t meant to be a “teach C# basics” forum, it’s about using C# with Unity. Your question is about the language itself and there are lots of resources online about C#.

I thought this was important to point out.

4 Likes

I guess I’m not really sure what you’re not understanding here.

A reference is just that, a reference to something in the heap. If you have no reference to something on the heap, you can’t do anything with it. If nothing is referencing it at all, it is inaccessible and will inevitably be garbage collected.

You can’t change what a reference is pointing to without having access to said reference as well.

If you want to keep a reference to something… keep a reference to it. That’s all there is to it.

2 Likes

I was bored this morning, procrastinating or something.

Variables can really only be two kinds: a value or a reference.

Local variables have names declared in the code, and a chunk of indivisible memory allocated on the “stack” to hold the value. That means that when the code reaches the next } the memory is unallocated immediately and that variable ceases to exist. In the image below, A and B and C are values, and E is a reference.

In C#, all core types like int and float and double and char are value types. Also, all struct types are considered values, and will be fully copied whenever you make an assignment to a variable, or return a struct type from a function. This implies that you will never be able to have more than one variable name associated with a value. When you assign one value variable to another value variable, you are copying the value and therefore they are two distinct copies of the value. Changing one later won’t change the other copy.

It’s okay to consider the “value” of a reference variable as being an arrow that goes somewhere. If it doesn’t go anywhere in particular, it goes to a magical ever-present place called null. The value of a reference can be changed, but it must always be pointing somewhere, and can only be pointing to a single place in memory.

Now for classes and instances. Let’s say we have a class called Foo. Inside that class definition, we have designed the class Foo to have two member variables. Unlike local variables which are stored on the stack, member variables will remain in memory for as long as the instance remains in memory. They don’t disappear at the next end of a code block } like local variables.

Unlike the class, instances do not have names at all. They are nameless. They exist in memory without any name in your code at all. This is an important detail: they live in a part of the memory called the “heap” and have NO PERMANENT NAME associated. If you want to give it a variable name, you use a reference variable like Foo E above, but that name is for your code to remember, and is not permanently associated with the instance at all. When you say new Foo(), a new instance is constructed and floats about freely in memory. It is only by saying F = new Foo() that the instance has a lifeline to the living world of variables and will remain allocated. If you didn’t save a reference to it in a reference variable, the orphaned memory of that instance would be discovered by the garbage collector and destroyed and freed up for other purposes. The member variables X and Y inside this particular instance of class Foo would also be destroyed, they would cease to exist.

If you have two reference variables, they can both point to the same instance of a class in memory. Both reference variables have an equally strong attachment to that instance, as long as their arrows point to it. Both reference variables keep the instance alive. It would require you to sever both connections before the instance is destroyed. This is why it’s important to understand that an instance has no variable name. It can be known by many different reference variables with equal importance. In C#, you can usually say that two reference variables that point to the same instance are equal, but this language rule can be overruled and changed by the code of the class involved.

Let’s add a whole different class Bar. We will create an instance of Bar with new Bar(), and attach it to the world of the living by assigning a reference to it with Bar H = new Bar(). Inside class Bar, we have declared there is a member variable called Z, which is a reference variable. It starts with no particular instance of Foo in mind, so it points to null.

And finally, let’s make the instance of class Bar which is referred by variable H hold onto something. By attaching H.Z to refer to our existing instance of class Foo, we now have three whole aliases by which we know the otherwise anonymous instance of Foo in memory. It would take the disconnection of all three references to that instance, for that instance to be destroyed. Even if both of the local variables F and G were to hit the end of their scope and be erased, if the instance of Bar remains in memory, the instance of F is safe.

I recommend that whenever you’re unclear about scope and lifetime and instances and references, draw yourself a diagram something like this.

4 Likes

To wrap this up, I try to explain how variables and references work as best as I can. I understand that there are some programming languages out there (especially interpreted scripting languages) which behave in a really odd way which may teach you some strange ideas about how computers work. One such languages which I use at work on a daily basis is PHP which has some really messy behaviour when it comes to instances and when shallow copies happen. Even things like arrays when passed around can be copied automatically which is such a weird concept.

C#, even though it uses a quite different memory management as languages such as C or C++, behaves actually quite similar to C and most other languages. A “reference” is not really the same as a “pointer” but from a pure functional point of view it behaves almost the same and is a good mental image when you try to reason about it.

Class instances
A class instance is never created automatically in a hidden way. Class instances need to be created with “new” (*1) which would allocate a chunk of memory on the heap where the class instance is initialized. As I said, references are not really pointers but you can think of them like that. So just imagine that a reference is actually the memory address where the instance is located in memory. You can use this address to “reach out” to the instance and interact with it. The instance is never “moved” as it is located at this specific address.
(*1)

I said all class instances have to be created with “new” and that is true without exception. However, especially in Unity we have seemingly some “exceptions” to that rule, especially when we talk about Components and MonoBehaviours. However that’s not actually true. Even Component instances have to be created with “new”.Though since Components have a native C++ counterpart in the Unity core, the creation of the managed C# instance is handlled by Unity internally. So Component instances can only be created through the AddComponent method. It will actually create the native and managed instances and link them together internally. Also Components can’t live on their own as they always have to be part of a GameObject. That’s another reason why you can’t / shouldn’t create Components manually with new.

Technically you can create a MonoBehaviour class manually with “new”, but you never should because this instance would be a “dead” instance as it’s missing its native counterpart. So never actually do that.

Address / reference
An address is the equivalent of a real world address. Unlike a real world address that may consist of a country, city, zip code, street and house number, addresses in a computer are just memory addresses. On 32 bit systems an address has 32 bits, so it’s just a normal integer value. On 64 bit systems it’s a 64 bit integer (long) value. Managed references work a bit differently under the hood, but the concept is the same.

Variables
A variable is just a certain memory space that has been given a name / alias. To which memory location a variable name refers to depends on the scope of the variable. Variables can be located as part of an object (instance variable), as part of a class (static variable) or could be “allocated” on the stack (local variable). In any case a variable is always just a chunk of memory that holds “data”.

Variables of primitive types such as int, bool, float, char and even “structs” directly contain the value you store in that variable inside that memory region that the variable refers to. That’s why those types are called “value-types” as the variable memory directly contains the value you store in that variable. Variables of reference types do not contain the object itself but just the address / reference to that instance. That’s why they are called reference-types. So the “value” that is stored in a reference type variable is just the reference / address to an instance, or “null” (which is literally just the value zero 0)

Assignment / passing of variables
When you assign “something” to a variable, you store a value in that variable that matches its type (since C# is a type safe language). For value-types that means when you assign value to a variable, you simply copy that value into the variables memory. For reference type variables, when you assign an object or another variable to such a variable, you also just store the address / reference in that variable since that’s what is actually stored in that variable. The same thing happens when you pass variable as parameters to methods. The parameters of a method are simply local variables that temporarily live on the stack and values of the passed variables are copied into the local variables of the method. This is true for value type and reference type variables. That does not mean that an object is somehow duplicated. Remember that the content of a reference type variable is just the address of the object. So you will copy the address into the local variable. The method can use that address to reach out to the place on the heap where the actual object is stored and interact with that object.

So if you have two reference type variables a and b each of those variable has some memory area that holds the current reference / address of that variable. When you do

a = b;

it means you just copy the address that is currently stored in b into a. This has absolutely no effect on the object that might live behind that address. Note that the address could even be “null”, so it might not even be any object instance involved. Of course whatever was stored in a is simply overwritten. When “a” was referencing another instance, this would no longer be the case. This has no immediate effect on the instance that was referenced by “a”. However when the garbage collector scans through all references and no other variable would hold a reference to that instance it would notice that and the instance would be garbage collected.

Back to your code
In the code of your post #6 you had 3 variables: a, b and c. You also create exactly two instances of your class “myClass”. So you only have those two instances. The address of Instance1 is first stored in variable “a”. Then you copy that address into variable “b”. So now “a” and “b” both contain the same address / reference to instance1. After that you create instance2 and you store the address of that instance in variable “c”. In the end you again copy the address stored in “c” (which is a reference to instance2) to variable “b”. So after that variable “a” still references instance1 and variable “b” and “c” both reference instance2.

Final word about the ref and out keywords
You may come across methods which have parameters which have the ref or out modifier attached. Those parameters are treated differently. The local variables of those parameters are not “normal” variables but are actual pointers to the memory of the variable that was passed to the method. They act like the famous “double pointers” or “pointers or pointers” when we talk about reference types. How that is actually implemented is irrelevant. What you have to understand is that the local variable name essentially becomes a direct “alias” of the variable that you passed in. So anything you do with the local variable inside the method you actually do to the original variable. So when you store a value in that variable, you actually modify the content / value / address of the original. Though ref and out parameters requires that you use the ref keyword when calling the method. So you can not accidentally pass a variable to a method that could change your variable.

Many programmers mistake “reference types” as being “passed by reference” which is not true. Only “ref” parameters are actually “passed by reference”. They simply take the fact you can use a reference to reach out to the referenced object as being passed by reference. However passing by reference or passing by value is only concerned with the “variable”, not with its content. By default ALL arguments are passed by value. For reference types that means the address is copied into the local variable.

ps: I just noticed that @halley1 has the same idea of writing a “verbose” answer ^^. Hopefully this clears up some confusion for future readers.

5 Likes

Thank you very much for your extensive explanations, I will study them with care, I really appreciate it

1 Like