C# XNA pass by reference classes in constructor - c#

I'm making a vector graphics game in XNA. I've designed a Line class to rotate around a central point to help draw specific shapes. In order to maintain a single point of truth, is there a way to pass a reference to the center of the shape to all of the lines I create, so that updating the center's position will also update the lines' positions? I thought something like this would work:
class Line
{
private Vector2 start;
private double length;
private double angle;
public Line(ref Vector2 start, double length, double angle){
this.start = start;
this.length = length;
this.angle = angle;
}
}
class Obj
{
private Vector2 center;
private Line[] lines;
public Obj(){
center = new Vector2(50,50);
lines = new Lines[5];
for (int i = 0; i < 5; i++){
lines[i] = new Line(ref center,30, (i/5 * 2 * Math.PI));
}
}
}
but the lines do not update when I move the center. What am I doing wrong?

Although the struct is correctly passed by reference to Line, when you assign it internally:
public Line(ref Vector2 start, double length, double angle){
this.start = start;
}
You are actually taking a copy of the struct.
If you ever find yourself needing reference type semantics of struct beyond passing it to a single method then you likely need to reconsider using class.
You can either re-implement the type in a class or wrap the Vector2 in a class and use that:
class Vector2Class
{
public Vector2 Centre;
public Vector2Class(Vector2 inner)
{
Centre = inner;
}
}
class Line
{
private Vector2Class _centre;
public Line(Vector2Class centre)
{
_centre = centre;
}
}
Be aware that you are still working against a copy, but if you share the class you'll all be working on the same copy.
Personally, I would avoid the wrapper and make my own class for representing "centre". This is supported by the largely accepted idea that struct types should be immutable, but you seem to need to mutate the values to keep the representation true.
class CentreVector<T>
{
public <T> X { get; set; }
public <T> Y { get; set; }
}
This only lets you share the data, it doesn't actually notify the lines that the centre has changed. For that you would need some sort of event.

Edited with alternative solution
The problem you're having is because Vector2 is a value type, you're correctly passing it by ref in your methods parameter but then making a local copy of it with the assignment.
I'm not totally sure if you could maintain a pointer to Vector2 in the way that you're thinking but you could create your own Vector2 class that would be a reference type.
class ObjectVector2
{
public float X { get;set; }
public float Y { get; set; }
}
I would like to suggest a slightly different way to achieve the same result by holding a reference to the obj that the lines are a part of.
class Line
{
private Vector2 Center { get { return parent.center; } }
private double length;
private double angle;
Obj parent;
public Line(Obj parent, double length, double angle)
{
this.parent = parent;
this.length = length;
this.angle = angle;
}
}
class Obj
{
public Vector2 center;
private Line[] lines;
public Obj()
{
center = new Vector2(50, 50);
lines = new Lines[5];
for (int i = 0; i < 5; i++)
{
// passing the reference to this Obj in the line constructor.
lines[i] = new Line(this, 30, (i / 5 * 2 * Math.PI));
}
}
}

Related

Trigger "Set" when changed class object property C#

If I have a custom class Vector3 and I have a class object Myobject that use the vector3 class as a property and when I set a new vector
myobject.pos = new vector3(x,y,z); the set will trigger in this case Debug.Log("set") as expected but If I instead of creating a new vector I only want to change x but still have set triggered I would want to do myobject.pos.x = x but this will not trigger the set in MyObject but the set in my vector3 class for x is there any way of fixing this so set in MyObject triggers when I change only the "sub property" x of the vector3.
public class MyObject{
private Vector3 p_pos = new Vector3(0, 0, 0);
public Vector3 pos
{
get { return (p_pos); }
set
{
Debug.Log("set");
p_pos = value;
}
}
}
The only way I can think of doing it is to create a separate method in MyObject class like this MyObject.SetPosX(x):
public void SetPosX(double _x)
{
Debug.Log("set");
p_pos.x = _x;
}
But It is not a very clean way of doing it so would prefer another solution.
The way I see it could be implemented in a more structured way is to allow Vector3 notify someone when a property has been changed. My object class would be listening to and react when necessary.
In the code you provide it looks like overhead but in fact, it depends on a real situation.
Code is below
using System;
using System.Diagnostics;
public class Program
{
public static void Main()
{
MyObject myObject = new MyObject();
Vector3 vector3 = new Vector3(1, 1, 1);
myObject.p_pos = vector3;
myObject.p_pos.X = 5;
Console.ReadLine();
}
}
public class Vector3
{
public Vector3(double x, double y, double z)
{
X = x; Y = y; Z = z;
}
private double _x;
private double _y;
private double _z;
public double X
{
get
{
return _x;
}
set
{
_x = value;
OnPropertyChanged(EventArgs.Empty);
}
}
public double Y
{
get
{
return _y;
}
set
{
_y = value;
OnPropertyChanged(EventArgs.Empty);
}
}
public double Z
{
get
{
return _z;
}
set
{
_z = value;
OnPropertyChanged(EventArgs.Empty);
}
}
public event EventHandler PropertyChanged;
protected virtual void OnPropertyChanged(EventArgs e)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
public class MyObject
{
private Vector3 _p_pos = null;
public Vector3 p_pos { get
{
return _p_pos;
}
set
{
if (_p_pos != null)
_p_pos.PropertyChanged-= _p_pos_PropertyChanged;
_p_pos = value;
_p_pos.PropertyChanged += _p_pos_PropertyChanged;
}
}
private void _p_pos_PropertyChanged(object sender, EventArgs e)
{
Console.Write("_p_pos_PropertyChanged");
}
}
The other way which could be more preferable is to make vector3 immutable so each time you need to change the value you have to create the new instance of vector3. This should allow you to stay with the lightweight version of vector3 and still have control over its value.
As the reason why it works like that seems not to be answered, I'll give it a shot.
public class TestClass
{
private Vector3 _v = 0;
public Vector3 V { get { return _v; } set { _v = value; } }
void DoSomething ()
{
V = new Vector3 (2, 0, 0); // This will trigger 'set'.
V.x = 2; // This will not.
}
}
If you have a Vector3 property and set its specific values, you set the values within the Vector3 class itself, but do not affect the assignment/reference to the said Vector3. In order to change its x value, you do not set a new or other Vector3 for 'V'. One way to circumvent that, as you already figured out, is simply to use a method to change the values of 'V' and trigger your extra code. Another one would be to have your own class which triggers your extra code when its properties are changed (for Vector3 not quite possible).
If you only want to track changes to Vector3s, you'd have to create something like a value Watcher class, which would be triggered in update to detect changes to values you want to track. To create this could be quite sophisticated and would involve lambda expressions and generics at least, possibly even an entire code structure in Unity by using a global base class for everything that shall inherit from MonoBehavior.

2D-array as a class property - how to create them the C# way?

I am trying to create a class with a property that is a two-dimensional array. The array will hold various x,y coordinates on a grid (e.g. 0,1 or 3,7) and the size of the array is dependent on a class property called size.
How would you go about creating this array in C#? I have given my solution below, but having very little C# experience and coming from a Python background with some javascript knowledge, it feels like that there is a better solution to this problem.
Could one of you C# wizards enlighten me, please?
Thanks in advance for your help.
Here is my code:
public class Obj
{
int Size; // Defines length of array
int[,] Pos;
// constructor
public Obj(int size)
{
this.Size = size;
this.Pos = new int[size, 2];
}
public void set_coord(int index, int x, int y)
{
if (index >= this.Size) {
Console.WriteLine("Catch OutOfRangeException");
}
else
{
this.Pos[index, 0] = x;
this.Pos[index, 1] = y;
}
}
You could create a List instead of a class, and have an internal sub class to represent your points.
Like this
public class Obj{
int Size;
List<Point> Pos = new List<Point>();
public Obj(int size){
this.Size = size;
}
public set_coord(int index, int x, int y){
if(index >= this.Size){
Console.Writeline("Catch OutOfRangeException")
}else{
this.Pos.Add(new Point(x,y));
}
}
}
class Point{
int x = 0;
int y = 0;
public Point(int xCor, int yCor){
this.x = xCor;
this.y = yCor;
}
}
A struct is the ideal approach for this. A full blown class may not be necessary, but it depends.
https://msdn.microsoft.com/en-us/library/ah19swz4.aspx
public struct Coordinates
{
public int coordX;
public int coordY;
}
The property then in your class could be set like this:
var Obj = new Obj();
List<Coordinates> listOfCoords = new List<Coordinates>();
var coord = new Coordinates();
coord.X = 20;
coord.Y = 15
listOfCoords.Add(coord);
Obj.Pos = listOfCoords
Keep in mind that Structs cannot be inherited from, or inherit, other classes or structs, as well as a few other gotchas. If you need these features, or the data in your struct is prone to modification after it is created (in other words, the data is NOT immutable), consider a small class instead.
https://msdn.microsoft.com/en-us/library/0taef578.aspx

c# how do I do a private set property?

I am creating a VECTOR object like so, but I am initializing it in the constructor:
public VECTOR position { get; private set; }
I am doing this operation:
position.x += 2;
VECTOR has a variable x defined as:
public double x { get; set; }
I get the error when I do the +=2 operation that says:
Cannot modify the return value of 'Projectile.position' because it is
not a variable
I want to be able to modify the position vector in the current class, and I want it to be accessible but not modifiable in other classes.
Probably, your problem is with the Vector class actually being a struct. Assume you have the following declarations:
public class Projectile
{
public VECTOR position { get; private set; }
public Projectile()
{
position = new VECTOR();
}
}
public struct VECTOR
{
public double x {get; set;}
}
You cant edit properties of the position property directly because you are accessing a copy of that field (explained here).
If you don`t want to convert your VECTOR into a class you can add a method that updates the position of your projectile:
public void UpdatePosition(double newX)
{
var newPosition = position;
newPosition.x = newX;
position = newPosition;
}
That will create a copy of the position, then update its x property and override the stored position. And the usage would be similar to this:
p.UpdatePosition(p.position.x + 2);
Expose the X property (and others) of VECTOR in a class.
public class myObject {
private VECTOR position;
public double X { get{return position.x;}set{position.x=value;}}
}
Usage example:
myObject.X += 2;

Get/Set The Members of a Struct or a Class in a Class C#

If I have a Struct or a Class, lets say I'm using a Vector2 (which has two members, float X and float Y), how am I supposed to properly get/set its methods in a class?
This is the code I know of right now:
public class MyClass
{
private Vector2 vector; //Has to be private
public Vector2 Vector
{
get { return vector; }
set { vector = value; }
}
}
But what if I wanted to edit the members (or certain members) of the Vector2 with the set method? Something like this is what I'm asking for:
set.X
{
vector.X = value.X;
}
set.Y
{
vector.Y = value.Y;
}
And it would be called as simply as Vector.X = 5. I thought up an alternative, using something like public float VectorX { set { vector.X = value; } } but I would prefer a more logical and object-orientated way. Is there?
It makes a BIG difference whether Vector2 is a class or a struct.
Since Vector2 is a class you can simply do
obj.Vector.X = 5;
having
public class MyClass
{
private Vector2 _vector; //Has to be private
public Vector2 Vector
{
get { return vector; }
set { vector = value; }
}
}
If, however Vector2 was a struct then you cannot modify the return value from the get. If you try you will get a compile error:
Cannot modify the return value of ... because it is not a variable.
You can work around this using the method you suggested with
public float VectorX
{
get { return _vector.X; }
set { _vector.X = value; }
}
public float VectorY
{
get { return _vector.Y; }
set { _vector.Y = value; }
}
or you could provide a wrapper class around Vector2 like:
class Vector2Wrapper
{
public Vector2 Vector;
}
and then store a Vector2Wrapper in MyClass like
public class MyClass
{
private Vector2Wrapper _vector2Wrapper;
public Vector2Wrapper VectorWrapper
{
get { return _vector2Wrapper; }
set { _vector2Wrapper= value; }
}
}
and you can then modify it like
obj.VectorWrapper.Vector.X = 5;
you can't specify a submethod to handle that partial set, because that set is handled by the Vector class and us thus out of your scope. the moment someone calls Myobject.Vector they are calling your get funcion, but when it moves on to .X they are calling the Vector.get_X function.
This may be easier to see by looking at the compiled code using the ILDasm tool, which reveals the actual method calls your properties make sugar out of.
Now, what you CAN do is to wrap certain properties, as mentioned. The result would like like so.
public class MyClass
{
private Vector2 _vector; //Has to be private
public Vector2 Vector
{
get { return vector; }
set { vector = value; }
}
public float VectorX
{
get { return _vector.X; }
set { _vector.X = value; }
}
public float VectorY
{
get { return _vector.Y; }
set { _vector.Y = value; }
}
}
Another option might be to use the pattern of INotifyPropertyChanged in your Vector class, raising an event every change which your MyClass can then listen to and react to, thus applying logic when child elements are updated.
There is also the option of using an indexer property
public class MyClass
{
public enum Axis { X, Y }
private Vector2 _vector; //Has to be private
public Vector2 Vector
{
get { return vector; }
set { vector = value; }
}
public float this[Axis axis]
{
get { return axis == Axis.X ? vector.x : vector.y; }
set
{
if(axis == Axis.Y)
{
// Special logic here
vector.Y = value;
}
if(axis == Axis.X)
{
// Special logic here
vector.X = value;
}
}
}
}
Because Vector2 is a struct, you are getting a COPY. You need to use the setter to set a new Vector2. There are 2 ways:
Vector2 v = myClass.Vector; //get a copy of the vector
v.X = 5f; // change its value
myClass.Vector = v; // put it back
I don't really like the following, but it's one statement:
myClass.Vector = new Vector2(2f, myClass.Vector.Y)
Inside MyClass, you can create a property that sets only the X value:
public float X {
get { return Vector.X; }
set {
Vector2 v = Vector;
v.X = value;
Vector = v;
}
}
(Vector can be an automatic property)

overloading the indexer causes : "Minesweeper.Tile is a type but is used like a variable" error

I am making a minesweeper project in c# for fun and I wanted to store the new Tiles in a dictionary inside the Tile class so that when a Tile is initiated it is stored and can be accessed through Tile[coords] however I keep getting the above error. This is the code I am using for the Tile class (please don't comment on my conventions, I'm new to C# I'm a Java/Python programmer :p)
class Tile
{
private static Dictionary <Coords, Tile> tiles = new Dictionary <Coords, Tile> ();
public int numMinesAdjacents { get; set; }
public readonly bool isMine;
public readonly Coords position;
private Tile [] aAdjacents = new Tile [8];
public Tile(int x, int y, bool isMine = false)
{
this.isMine = isMine;
position = new Coords(x, y);
Tile[position] = this;
}
public void init()
{
calculateAdjacents();
calculateNumMinesAdjacent();
}
private void calculateAdjacents()
{
int i = 0;
for (int y = -1; y < 1; y++)
{
if ((position.y - y) < 0 || (position.y + y) > Math.Sqrt(Program.MAX_TILES)) continue;
for (int x = -1; x < 1; x++)
{
if ((position.x - x) < 0 || (position.x + x) > Math.Sqrt(Program.MAX_TILES)) continue;
aAdjacents [i] = Tile[position + new Coords(x, y)];
i++;
}
}
}
private void calculateNumMinesAdjacent()
{
int n = 0;
foreach (Tile pTile in aAdjacents)
{
if (pTile.isMine) n++;
}
numMinesAdjacents = n;
}
/*private static void add(Tile pTile)
{
tiles.Add(pTile.position, pTile);
}*/
public /*static - if I use static nothing is different*/ Tile this [Coords coords]
{
get { return tiles [coords]; }
}
}
if I call
Tile(0, 0);
Tile(0, 1);
and then
Tile[new Coords(0, 0)]
I get an error, I also get an error in the places in the class where Tile[] is used (constructor and calculateAdjacents) what is going wrong here?
Thanks,
Jamie
EDIT: Sorry I meant Tile[position] I was changing it back and mistyped. The problem is I overloaded this which should mean that Tile[coords] is legal even when called from another class
It's not clear what you expect this to mean:
Tile[this];
It's not a valid expression at the moment though.
C# doesn't support static indexers. For an instance indexer, you could use:
Tile tile = this[someCoordinate];
... although it's odd for an instance indexer to use a static member like this. It would be cleaner just to have a method:
public static Tile GetTile(Coords coords)
{
return tiles[coords];
}
Then you'd just call Tile.GetTile(...) elsewhere.
As a side-note, you should start following .NET naming conventions to make your code easier to understand. Also, I'd strongly recommend that you avoid public fields, even if they're read-only.

Categories