Confused about object references in C# - c#

Ok,
I have a matrix of enemies Enemy enemyGrid[x, y]
Then, in my code I get an instance of one of the enemies by calling Enemy tmp = enemyGrid[a, b]
But if I change a property in tmp it is not reflected the next time I load the object from the matrix into the the same object tmp.
Each time I am finished with tmp I need to make it = null to have the change reflected into the object in the gird?
Why is that? I thought that tmp would just hold a reference to the object and changes would be made directly in the main object.
Thanks.
CODE UPDATE:
Populating the grid:
Enemy [,] spriteGrid = new Enemy[countCols, countRows];
spriteGrid[x, y] = new Enemy();
Access an object and change properties:
Enemy tmp = spriteGrid[i, j];
tmp.canShoot = true;
tmp.Update(gameTime, game.Window.ClientBounds);
tmp.canShoot = false;
The last line (canShoot = false) does not reflect into the object stored in the grid.

The line
Enemy tmp = enemyGrid[a, b]
does not create a copy of the object in your matrix. It creates an alias to the same object instance. Changes to tmp do affect the instance in the grid that they alias.
Please post a short, complete code snippet that demonstrates the issue you are experiencing.
UPDATE
In your sample, you set
tmp.canShoot = true;
but then
tmpEnemy.canShoot = false;
Two different variables.
Update 2
#Amry's comment is also accurate... if Enemy is a struct instead of a class, you would see this very behavior. That is because struct is a value type, meaning the assignment does create a copy rather than an alias.
Except for very special cases, you should never use a struct that is mutable (that is, a struct whose value can change after it is initially created).

Related

Struct variables not changing

I feel like I'm missing something complete obvious so I apologise in advance if (when?) that is the case.
I'm trying to do something really simple, change a bool value in a struct from false to true. Obviously I can't change it directly, so I created a method within the struct that I can call which should change the value there. It doesn't seem to be the case. Here is the code and I'd appreciate any insight;
public Dictionary<int, List<ScanLineNode>> allScanLineNodes = new Dictionary<int, List<ScanLineNode>>();
public void MethodName(ScanLineNode node [...])
{
//This will perform a raycast from the Node's position in the specified direction. If the raycast hits nothing, it will return Vector3.zero ('Row is complete'), otherwise will return the hit point
Vector3 terminationPoint = node.RaycastDirection(node, direction, maxDist, targetRaycast, replacementColour, backgroundColour);
ScanLineNode terminationNode = new ScanLineNode();
//Previously attempted to store a local reference to this row being used, but also did not work
//List<ScanLineNode> rowNodes = allScanLineNodes[node.rowNumber];
[...]
if (terminationPoint == Vector3.zero)
{
//Definitely reaches this point, and executes this function along the row, I have added breakpoints and checked what happens in this for loop. After running 'RowComplete' (which just changes 'rowComplete' from false to true) 'rowComplete' is still false. Just in case I've included the RowComplete() function below.
Debug.Log("Row Complete: " + node.rowNumber);
for (int i = 0; i < allScanLineNodes[node.rowNumber].Count; i++)
{
allScanLineNodes[node.rowNumber][i].RowCompleted();
}
}
}
ScanLineNode Struct -- Most stuff is hidden (that I don't believe is affecting this), I have included the RowComplete() function however.
public struct ScanLineNode
{
[...]
public bool rowComplete;
[...]
public ScanLineNode([...])
{
[...]
rowComplete = false;
[...]
}
public void RowCompleted()
{
rowComplete = true;
}
}
I have also confirmed that RowCOmpleted() does not get called anywhere aside the above location, and 'rowComplete' is only called from the RowComplete() function
(from comments) allScanLineNodes is a Dictionary<int, List<ScanLineNode>>
Right; the indexer for a List<ScanLineNode> returns a copy of the struct. So when you call the method - you are calling it on a disconnected value on the stack that evaporates a moment later (is overwritten on the stack - this isn't the garbage collector).
This is a common error with mutable structs. Your best bet is probably: don't make mutable structs. But... you could copy it out, mutate it, and then push the mutated value back in:
var list = allScanLineNodes[node.rowNumber];
var val = list[i];
val.RowCompleted();
list[i] = val; // push value back in
But immutable is usually more reliable.
Note: you can get away with this with arrays, since the indexer from an array provides access to a reference to the in-place struct - rather than a copy of the value. But: this isn't a recommendation, as relying on this subtle difference can cause confusion and bugs.

How to make an interactive objects using PhotonNetwork?

On the stage, I create several objects using PhotonNetwork.Instatiate ();
On these objects there is a script that changes the transform of the object. In the same script, a random variable is selected from the list, depending on which the transform is changing.
Now the question. How do I make this object change its transform simultaneously and equally for all players?
I'm thinking of doing something like this: make a separate script that will select a random number -> in it, write something like this
if (view.isMaster) // ???????
for (int i = 0; ......) {
objectInList [i] .myFloatValue = randomValueInArray;
objectInList [i] .canToChange = true;
}
And in the script of a mutable object, write something like:
if (canToChange)
// change the position;

Cant set control.Location.x to argumentEvent e.x

I am trying to set the location of the control after some event based on the location of EventArg pictureBox1.Location.X = e.X;.
However this does not work it Cannot Modify expression because it is not a variable. But i was under the impression that x coordinate is a property and can be set. What is going on here ?
Because System.Drawing.Point is a value type, when you call pictureBox1.Location, you are actually getting a copy of the Point. A whole new object is constructed and filled with the fields of pictureBox1.Location.
As such, the compiler is trying to protect you from doing something silly, as changing the value of the copy would not propagate to the value of Location.
As such, and as mentioned in the other answers, you should construct a new Point and assign it to the Location property.
Try this instead:
pictureBox1.Location = new Point(e.X, pictureBox.Location.Y);
or if you don't want to construct a new variable:
Point location = pictureBox1.Location;
location.X = e.X;
pictureBox1.Location = location;
This is because Point is a value type, and therefore you can't just edit one of its values, as it won't propagate. Its value is stored, not a reference to the value. You can't just edit it, you need to build the object again. This could compile, but it would do absolutely nothing, in no possible scenario, so the compiler makes sure you don't do this error.
Some people here talk about Point is a value type and you can't change its X and Y, that kind of explanation can confuse you much. I post this answer here to help you understand why you can't change it's Location. That's because Location is a Property which returns a Structure not a reference to an Object, if you have a field instead, you can change that way, like this:
public class YourControl : BaseControl {
public Point Location;
}
//Then you can change the Location your way:
yourControl.Location.X = ....
However, as I said, Location is a Property which returns a copy of a value type (structure), like this:
public class YourControl : BaseControl {
private Point location;
public Point Location {
get {
return location;//a copy
}
set {
location = value;
}
}
}
//So when you call this:
yourControl.Location
//you will get a copy of your Location, and any changes made on this copy won't affect
//the actual structure, the compiler needs to prevent doing so.
yourControl.Location.X = ... //Should not be executed...
This is not the only case for Location, you can find this issue in all other Properties which are value types.

C# List Objects

I have a list of Character objects I made and a list of Cells which can contain one character. Is it possible for my Character object to be added to the list and be assigned to a cell and changes I make to it in the party list or the cell to effect the object in both place? I don't really know how the pointers will work out for this. I figure what will happen is the object in the list is a separate object from the one assigned to the cell.
Sorry my code is very large so I don't want to post it all here but the Character I am talking about is a custom class I made. I suppose my question really boles down to two questions. When I put something in a list is that changing where the pointer points or is that a new object all together. Also can I have multiple pointer if I add something to a list then assign that to another instance of the character object will referencing the Character from the Cell Object be the Same as referencing my Character from the other list object.
As long as your Character is a class (i.e. a reference type) then you are essentially storing references to Character objects. Any changes you make to an object through a reference to it will be visible when the object is accessed through any other reference.
Example:
class Character
{
public string Name { get; set; }
}
var c = new Character();
var c2 = c;
var arr1 = new Character[] { c };
var arr2 = new Character[] { c };
arr1[0].Name = "Foo";
Console.WriteLine(arr2[0].Name); // "Foo"
Console.WriteLine(c2.Name); // also "Foo"
So basically you want to treat Characters as a reference type.
Just create your own wrapper object which contains a character inside it.
public MyCharacter
{
public char character{get;set;}
}
Objects are treated by reference, so that should work for you.

C# Calling a Method, and variable scope

Why is cards being changed below? Got me puzzled.. understand passing by ref which works ok.. but when passing an Array is doesn't do as I expect. Compiling under .NET3.5SP1
Many thanks
void btnCalculate_Click(object sender, EventArgs e)
{
string[] cards = new string[3];
cards[0] = "old0";
cards[1] = "old1";
cards[2] = "old2";
int betResult = 5;
int position = 5;
clsRules myRules = new clsRules();
myRules.DealHand(cards, betResult, ref position); // why is this changing cards!
for (int i = 0; i < 3; i++)
textBox1.Text += cards[i] + "\r\n"; // these are all new[i] .. not expected!
textBox1.Text += "betresult " + betResult.ToString() + "\r\n"; // this is 5 as expected
textBox1.Text += "position " + position.ToString() + "\r\n"; // this is 6 as expected
}
public class clsRules
{
public void DealHand(string[] cardsInternal, int betResultInternal, ref int position1Internal)
{
cardsInternal[0] = "new0";
cardsInternal[1] = "new1";
cardsInternal[2] = "new2";
betResultInternal = 6;
position1Internal = 6;
}
}
Arrays are reference types which in short means the value of the array is not directly contained within a variable. Instead the variable refers to the value. Hopefully the following code will explain this a bit better (List<T> is also a reference type).
List<int> first = new List<int>()( new int[] {1,2,3});
List<int> second = first;
first.Clear();
Console.WriteLine(second.Count); // Prints 0
In this scenario there is a List<int> created on the first line which is referred to by variable first. The second line does not create a new list but instead creates a second variable named second which refers to the same List<int> object as first. This logic applies to all reference types.
When you pass the variable cards into the method you do not pass a copy of the full array but instead a copy of the variable cards. This copy refers to the same array object as the original cards. Hence any modifications you make to the array are visible through the original reference.
A variable of a reference type does
not contain its data directly; it
contains a reference to its data. When
you pass a reference-type parameter by
value, it is possible to change the
data pointed to by the reference, such
as the value of a class member.
However, you cannot change the value
of the reference itself; that is, you
cannot use the same reference to
allocate memory for a new class and
have it persist outside the block. To
do that, pass the parameter using the
ref or out keyword.
http://msdn.microsoft.com/en-us/library/s6938f28(VS.80).aspx
When you are passing a reference type (like an array) to a method by value, you are passing a copy of it's reference. It's still the same object that is referenced, it doesn't create a copy of the array itself.
When passing parameters to methods, there are three different concepts to be aware of:
By Value vs By Reference parameters
Value vs Reference types
Mutable vs Immutable types
In your example, the string array is a Reference type, is a Mutable type, and is passed By Value. The compiler will always let you change the content of the array because it is Mutable. However, since it is a Reference type, the calling code and the called code both point to the same array contents, so the calling code "sees the changes". The fact that it's passed by value in this case is irrelevant, since although the called code's array variable has indeed been passed a copy of the calling code's variable, they both point to the same location in memory.
As other answers have said, it's because a reference is being passed by value.
I have an article on argument passing in C# which you may find useful, in addition to the answers here.
Arrays are reference types, thus are subject to change.
When you are passing an array as an object it is not copied. The receiving method works with the same instance. In a sense arrays are always passed by ref. When an array as well as an instance of any other reference type is passed as a parameter the receiving method gets its own copy of a reference on the same instance of the type. No copy of the actual object is created.
If you need to pass a copy you have to be explicit about this: create a copy yourself or clone the array. The reason it is not done for you is obvious - copying an array can be expensive, you do not want it unless it is really necessary

Categories