Structs: field must be fully assigned compiler error - c#

I've declared the following struct:
struct StartPositions
{
public Vector2 pacman;
public Vector2[] ghosts;
// Constructor accepts a Vector2, and an array of Vector2's.
public StartPositions(Vector2 pacmanPosIn, Vector2[] ghostsPosIn)
{
pacman = pacmanPosIn;
for(int a=0;a<ghostsPosIn.Length;a++)
{
ghosts[a] = ghostsPosIn[a];
}
}
}
However I get a compiler error saying the ghosts field must be fully assigned. What I want to do is pass in a Vector2, and a Vector2 array when I create a StartPositions object - making a copy of that array.
How can I do this correctly?

You did not initialize the ghosts array. You need to add a call to new.
public StartPositions(Vector2 pacmanPosIn, Vector2[] ghostsPosIn)
{
pacman = pacmanPosIn;
ghosts = new Vector2[ghostsPosIn.Length];
....
}
And you can simplify the code by replacing the for loop with a call to Array.Copy().
Array.Copy(ghostsPosIn, ghosts, ghosts.Length);

You have to initialize your ghosts array first:
struct StartPositions
{
public Vector2 pacman;
public Vector2[] ghosts;
// Constructor accepts a Vector2, and an array of Vector2's.
public StartPositions(Vector2 pacmanPosIn, Vector2[] ghostsPosIn)
{
pacman = pacmanPosIn;
ghosts = new Vector2[ghostsPosIn.Length];
for(int a=0;a<ghostsPosIn.Length;a++)
{
ghosts[a] = ghostsPosIn[a];
}
}
}

You didn't initialize ghosts array.
public StartPositions(Vector2 pacmanPosIn, Vector2[] ghostsPosIn)
{
...
ghosts = new Vector2[ghostsPosIn.Length];
...
}
From C# Language Specification;
Actual array instances are created dynamically at run-time using the
new operator. The new operation specifies the length of the new array
instance, which is then fixed for the lifetime of the instance.

One annoying quirk in .net is that unless one is using "unsafe" code the concept of a value-type array does not exist. The struct as shown contains a position for the "pacman" and a reference to a mutable array that holds the positions of the ghosts. This is an evil combination, since the struct may appear to encapsulate the positions of the ghosts, but it does not. Thus, if one were to say:
StartPositions p1 = whatever();
... do some stuff
StartPositions p2 = p1;
p2.pacman.X += 3;
p2.ghosts[0].X += 3;
the code would add three to p2.pacman and p2.ghosts[0]; it would not affect p1.pacman.X but would add three to p1.ghosts[0]. Such behavior would likely cause confusion.
If your intention is that StartPositions will be read-only, it should probably never expose the ghosts array directly; instead, ghosts should be a property of type IList<Vector2>, and your constructor should set it to something like a new ReadOnlyList<Vector2> initialized with a copy of the passed-in positions. If it does that, then ghosts can simply be a read-only property that returns such positions.

Related

Unnecessary assignment of a value to 'player"

I am having a problem assigning values in my function. Here is my code
//Player cents
private int add_cents = 3;
public int player_1, enemy_1, enemy_2, enemy_3;
public void players_ready()
{
add_cents_player(player_1, add_cents);
}
public void add_cents_player(int player, int cent_v)
{
player = player + cent_v;
}
I want to be able to call this function and input whoever is the active player (player) and increase their value by (cent_v). However, player = player + cent_v; is saying "Unnecessary assignment of a value to 'player" and I don't understand why. It wouldn't be possible to hard code, as it is dependant on what who is the active player.
One option is to change the method return type:
private int add_cents = 3;
public int player_1, enemy_1, enemy_2, enemy_3;
public void players_ready()
{
player_1 = add_cents_player(player_1, add_cents);
}
public int add_cents_player(int player, int cent_v)
{
return player + cent_v;
}
int is a value type. It is passed by value meaning the player will actually be a copy of player_1. If you then change the player inside your method this doesn't affect in any way the player_1 since it is no reference and no relationship between them.
It looks like what you wanted to do would be using ref in order to "force" the value to be passed by reference
public void players_ready()
{
add_cents_player(ref player_1, add_cents);
}
public void add_cents_player(ref int player, int cent_v)
{
player += cent_v;
}
thus that after calling players_ready the value player_1 is actually increased
There are 2 ways to pass a variable to a function. 1 is by reference, meaning you pass a reference to a variable into the function. This is what happens with variables of type object - not the whole object's memory is copied and supplied to the function, but only an address to the piece of memory where that object resides.
For int, float etc. this is different. The values are passed by value.
Also see the relevant msdn docs.
There is a fundamental difference between the two options: reference types are passed by reference and can be altered and the original object also gets altered. E.g. this works:
class MySimpleObject // an object is a reference type
{
public int someValueType; // int is a value type
}
...
var x = new MySimpleObject();
myFunc(x); // increment x.someValueType by 5
This does not count for objects passed by value, which is what happens with int, as its a value type. Therefore your function does nothing, because its only manpulating the local value, the reference is not passed.
var player_1 = 5;
add_cents_player(player_1, 15);
// Player_1 is still 5
add_cents_player(player_1, 15);
// Player_1 is still 5
...
And thats what the compiler is complaining about; you're adding a value to the local parameter in the function. But since you don't return this, or read the value at some point the compiler is like 'hey this code does nothing, and therefore its better to remove it.

how does initializing structs work in c#?

im currently going through a game tutorial in c# and since i dont know almost anything about the language simple stuff looks confusing.
in my class i have something like this:
[Serializable]
public struct TileStruct
{
public TileDataStruct[] Layer;
public byte Type;
public int Data;
public byte DirBlock;
}
public TileStruct[,] Tile;
first question is what does [,] mean here?
second, it is initialized like this:
Tile = new TileStruct[(byteValue), (byteValue)]
1) why are two values passed to TileStruct? Where do they end up?
2) why are the values passed in parentheses?
public TileStruct[,] Tile;
This part declares public variable named Tile. TileStruct[,] means this variable holds a reference to a two dimensional array which contains instances of TileStruct. You can read more about this here.
Tile = new TileStruct[(byteValue), (byteValue)]
This line creates new instance of two dimensional array I've mentioned earlier and assigns it to Tile variable. [(byteValue), (byteValue)] declares size of this array in each dimension. Those values are not passed to TileStruct in any way. There's no need for parentheses around byteValue and deleting them won't change anything.

Is there a reason for array declaration syntax in C#?

I was wondering what's the reason behind the syntax? I mean it's like this:
int[ , ] arrayName = new int[10,10];
Wouldn't it be simpler if we had something like:
int[10,10] arrayName ;
What caused the software architect to make such a decision?
It's because you can declare the variable in one scope, and initialize it in another (normally in a constructor).
Something like this:
public class Foo
{
private int[ , ] Bar; // Bar will be null until initialized
public Foo(int a, int b)
{
Bar = new int[a, b];
}
}
With int[10,10] arrayName; you are committing to the array size. What would happen if I did this?
int[10,10] arrayOne;
int[11,11] arrayTwo;
arrayOne = arrayTwo;
An array object is simply a pointer to a block of managed memory. Knowing this, arrayOne = arrayTwo would have to be technically legal.
Therefore, stating that it as an int[10,10] is actually a lie. It may start that way, but it could at any time be reassigned into an array of different dimensions.
This
int[ , ] arrayName = new int[10,10];
already initializes the variable in memory and instantiate it. Also, you could change the size of the array during runtime, for example
arrayName = new int[20,20]
On the other hand
int[10,10] arrayName ;
Will just declare the variable for the array. arrayNamethen is still null.
int[,] arrayName = new int[10,10];
Only the left hand side is declaring the array, the right hand side is initialising it. This is true for most (if not all) of variables in C#. For example
string test; //declaring
test = "test"; //initialising
string test = "test"; //declare and initialise

What's wrong with my array declaration?

The following works:
int[] numbers = new int[2];
The following doesn't:
int Size = 2;
int[] numbers = new int[Size];
Error: "A field initializer cannot reference the non-static field, method, or property"
I know this must be me doing something stupid but can't work out what it is at all.
You can give size of array by constant but not variable as the size of array could not be given at class level if you declare the array out side the method at class level. C# does not guarantee the order in which fields within a class are initialized so you cannot use one non-static field to initialize another non-static field outside of a method, reference.
const int Size = 2;
int[] numbers = new int[Size];
void SomeFun()
{
}
If you declare it inside some method then you wont get error.
void SomeFun()
{
int Size = 2;
int[] numbers = new int[Size];
}
You can use list instead of array if your collection size in unknown at runtime.
List<int> lst = new List<int>();
Put these initializations into the constructor:
public class MyClass
{
const int Size;
int[] numbers;
public MyClass()
{
this.Size = 2;
this.numbers = new int[this.Size];
}
}
In this way you are providing a guarantee of order in which initializations should be executed.
You cannot use an instance variable to initialize another instance variable. There is no guarantee that the 'Size' variable will be initialized before the 'number' array. The initial values for fields need to use constants - it's not as restrictive as that; they can also reference static fields, methods or properties.

Not able to modify object of struct in loop

I have a List of structure.In the loop i am trying to modify the object's property,which is happening,but when i (Quick look in Visual studio)look into the list object ,the new value is not reflecting.Is it by virtue that the structure's object cannot be modified when in a collection?
I am using generics list with the struct as the type in the list
You mention "modify the object's property" in the context of a struct, but importantly a struct is not an object. Other people have answered as to the issue with structs being copied (and changes discarded), but to take that further the real problem here is that you have a mutable (changeable) struct at all. Unless you are on XNA (or similar) there is simply no need.
If you want to be able to change properties, make it a class:
public class Foo {
public string Bar {get;set;}
}
This is now a reference-type, and your changes (obj.Bar = "abc";) will be preserved through the foreach. If you really want/need a struct, make it immutable:
public struct Foo {
private readonly string bar;
public string Bar { get {return bar; }}
public Foo(string bar) {this.bar = bar;}
}
Now you can't make the mistake of changing the value of a copy; you would instead have to use the indexer to swap the value (list[i] = new Foo("abc");). More verbose (and you can't use foreach), but correct.
But IMO, use a class. Structs are pretty rare, to be honest. If you aren't sure: class.
If you are using a foreach loop you probably got
Compiler Error CS1654
Error Message Cannot modify members of
'variable' because it is a 'read-only
variable type'
This error occurs when you try to
modify members of a variable which is
read-only because it is in a special
construct.
One common area that this occurs is
within foreach loops. It is a
compile-time error to modify the value
of the collection elements. Therefore,
you cannot make any modifications to
elements that are value types,
including structs.
You could however try
struct MyStruct
{
public int i;
}
List<MyStruct> list = new List<MyStruct>
{ new MyStruct { i = 1 }, new MyStruct { i = 2 } };
for(int i = 0; i < list.Count; i++)
{
MyStruct val = list[i];
val.i++;
list[i] = val;
}
EDIT
See also Structs Tutorial
Structs vs. Classes
Structs may seem similar to classes,
but there are important differences
that you should be aware of. First of
all, classes are reference types and
structs are value types.
I THINK i know what the problem might be.
struct Astruct
{
int amember;
}
List < Astruct > listofStructs;
foreach(Astruct A in listofStructs)
{
A.amember = 1337;
}
if this is what you are doing...
when you use structs in c# they are not referenced but copied! so that means the contents of your list is being COPIED to A, so when you change A it doesn't change the value in the list!
to solve this problem (if this is your problem...) either use CLASSES rather than STRUCTS, that way A would be a reference, OR use a manual iterating for loop instead, ie:
for(int i=0;i < listofStructs.Count;i++)
{
listofStructs[i].amember = 1337;
}
alternatively, if you’re using a list, you maybe should use an iterator or something... but the above should definitely fix that problem.
Given the information in your post (although I'd have liked to see the code itself), let me put forth the most probable issue and its fix.
foreach(var s in listOfStructs)
{
s.Property = x;
}
s is assigned to a copy of the actual struct in the collection. s.set_Property is now modifying the copy which is thrown away at the end of the current iteration.
This is because 2 value type variables cannot point to the same instance.
struct1 = new MyStruct(100, 200);
struct2 = struct1; // struct2 is now a copy of struct1
Now to the problem of how do you modify the instances in a collection:
Get the object to modify in a local variable (copy created). Modify it. Now remove the original object and insert the copy. use listOfStructs[i] = modifiedInstance.

Categories

Resources