Fast way to find a specific myClass across many List?

I have a simulation class which contains a list for each Room in the game, and each Room has a list of each NPC in that room. When the player enters a room every NPC the simulation says is in that room gets an instantiated model which is given a reference to that NPC’s CharacterSheet class in the simulation, so that anything that happens to the spawned character can be added to their local CharacterSheet. When the NPC despawns, I would like to re-locate the position of its CharacterSheet in the simulator, and set the simulator’s sheet equal to the local NPC’s sheet, effectively applying any changes that occurred (injuries, item acquisitions) while the NPC was spawned.

Is there a simple, fast way to do this I’m not thinking of? What I’m doing right now is giving each NPC a unique ID number in their CharacterSheet, and iterating through each Room until I find a character whose ID matches the one of the NPC I’m trying to despawn, but this is heinously slow already, and if I’m having to do it 10-20 times every time the PC enters a room, things would slow to a crawl.

Uhm, why don’t you hold a reference to your simulation class in a script attached to your character object? If your simulation class holds your actual values, why do you use a local CharacterSheet in the first place? When you create the visual representation of your NPC, you could simply assign it the reference to the simulation class instance that represents that NPC logically.

In reply to @BoredMormon’s comment:

Classes are passed by reference

Unfortunately “passing” the wrong terminology :slight_smile: A lot people mix up those terms and concepts and in the end most think they understood how it works but actually got it wrong.

When you assign or pass a variable to another variable or to a method it’s always “passed by value”. The only exceptions are “ref” and “out” parameters of methods.

When you assign a variable to another variable you copy the content of the variable into the other variable. For method parameters it’s exactly the same. What most people seem to struggle with is that the content of a variable can be a reference. That’s the case for all reference-types. Reference-types aren’t stored on the stack like local variables. You actually never get direct access to the content of a reference type (like a class instance) since you only can access this data indirectly by using a reference to that object.

Short example:

public class SomeClass
    public int i = 0;

SomeClass CreateObject()
    SomeClass myVariable = new SomeClass();  // (1)
    SomeClass myOtherVariable = myVariable;  // (2)
    myOtherVariable.i = 42;                  // (3)
    return myVariable;                       // (4)

// ...

SomeClass a = CreateObject();                // (5)
SomeClass b = a;                             // (6)
a = new SomeClass();                         // (7)

Inside the CreateObject method there are two local variables “myVariable” and “myOtherVariable”. At (1) we create a new instance of our “SomeClass” class. The new operator however does not return the object that is created, but a reference to that object. That reference is copied into “myVariable”. You can now use this variable to indirectly use that object that is referenced.

At (2) we copy the content of myVariable to myOtherVariable. Both variables now contains the same reference. At (3) we use our copy of the reference to indirectly access our object on the heap and change it’s variable “i” to 42.

At (4) we return the value of myVariable, which is the reference to our class instance .At (5) we receive the copy of that reference and copy it into the variable “a”. At the same time the variables myVariable and myOtherVariable go out of scope and extent (since we’ve left the method). Those variables don’t exist anymore. However our class instance still exists since we still have a copy of the reference stored in “a”. At (6) we again copy the content of the variable “a” to the variable “b”. Now at (7) when we overwrite the content of our variable “a” with a reference to a new instance we just created, the content of the variable “b” will stay the same since it’s an independet copy of the reference to our first object. When now reading “b.i” it would return “42” while “a.i” now returns “0” since the reference stored in “a” references the new object.

A struct on the other hand is a value type. The whole struct is actually stored directly in a variable as one big value / content. Vector3 for example is a struct and therefore value type. Let’s do something similar to what we just did with a class:

Vector3 v1 = new Vector3(1,2,3);     // (1)
Vector3 v2 = v1;                     // (2)
v2.x = 42;                           // (3)
Debug.Log(v1);                       // (4)

At (1) we initialize our v1 variable with a Vector(1,2,3) unlike classes structs are stored directly inside the variable. So the content of v1 is a Vector(1,2,3) and not a reference to some place on the heap. At (2) we again copy the content of the variable into our second variable “v2”. Now if we change the value of the “x” member of our v2 variable we directly modify the value which is stored inside v2. That means the content of v1 is not modified which you can prove at (4) since it would print (1,2,3).

So as said variable assignment or passing of a variable to a method will always copy the content of a variable. Now the exceptional case, the ref keyword. The ref keyword allows you to pass a variable by reference and not by value as usual. The point of this is that inside of that method you can actually directly modify the content of the variable. I’ve often read a statement like: “it makes no sense to pass a variable of a reference type by reference since you already can modify the object the reference references”. It’s true that you can modify the object behind the reference, but that’s not the point of the ref keyword. The ref keyword allows you to modify the content of a variable. Again, the content of a reference-type variable is the reference, not the object behind the reference.


void DoMagic(ref SomeClass aObj)
    aObj.i = 111;            (4)
    aObj = new SomeClass();  (5)
    aObj.i = 222;            (6)
SomeClass a = SomeClass();   (1)
SomeClass b = a;             (2)
DoMagic(ref a);              (3)
Debug.Log("a.i == " + a.i);  (7)
Debug.Log("b.i == " + b.i);  (8)

At (1) we create a new instance of our class and store the reference in variable “a”. At (2) we create a copy of the reference in “b”. Just to recap, both variables no point to the same object. At (3) we call our method “DoMagic” and pass the variable a by reference. Since the parameter is a ref-parameter we have to pass the variable like this.

Inside DoMagic at (4) we set the “i” variable of the object behind the reference to “111”. At (5) we overwrite the content of aObj with a new instance. Since aObj is a reference to the variable “a” we directly replace the content of the variable “a” outside of the method. At (6) we set the “i” variable of our new object to “222”. At (7) and (8)you can observe that it will print:

"a.i == 222"
"b.i == 111"

As you can see DoMagic hasn’t only changed the content of the variable “i” in our first object (where we still have a reference stored in “b”), but also replaced the reference that was stored in “a” with a new object.

If you would do the same without the ref keyword, your Debug logs would print:

"a.i == 111"
"b.i == 111"

That’s because if “aObj” is not a ref-parameter it’s an ordinary local variable of our method. You can use it at (4) to set the value of “i”, just like in the other example. However, at (5) you would only overwrite the content of the local variable which at the moment hold a copy of the reference to our first object. That wouldn’t affect the variable “a” outside of the method since the content of a has been copied inthe aObj. When DoMagic is done and returns to the main program the newly created class instance (with i set to “222”) is now marked for garbage collection since there is no reference left to that object.

Just as final note: You may ask yourself “How are ref parameters actually implemented in C#”, the answer is: by cheating :D. Usually pointers (not references, actual pointers) aren’t allowed in the managed environment. You usually have to use an unsafe context to mess around with pointers. However in this special case the compiler actually creates a pointer to the memory location where the variable is stored and passes that pointer (of course, by value, the address value) to the method. When using the “aObj” variable inside the method you working indirectly with the variable “a” through a pointer.

If you like reading great analogies you have to read this one by Eric Lippert (he’s one of the people who designed the C# language and has written major parts of microsofts C# compiler). Even this question was about C++ it applies to almost any language which has pointers.