How to iterate through a struct's fields? - c#

I have a simple struct that looks like this:
public struct GridNeighbours
{
public static Vector2Int North = new Vector2Int(0, 1);
public static Vector2Int South = new Vector2Int(0, -1);
public static Vector2Int East = new Vector2Int(1, 0);
public static Vector2Int West = new Vector2Int(-1, 0);
}
Is there a way to "iterate" through each of these fields somehow with a for loop? Do structs by chance index their fields or something? It would make my code a lot cleaner if it was possible but I am not sure how to make it iterative.
Edit: due to this being used in a hot path in a game, is there a way to rewrite this so i can avoid reflection ?
This is not a duplicate since i need an alternative approach to avoid reflection.

public struct GridNeighbours {
public enum Cardinal { North, East, South, West }
public static Vector2Int[] Neighbours = { new Vector2Int(0, 1), new Vector2Int(1, 0), new Vector2Int(0, -1), new Vector2Int(-1, 0) };
public Vector2Int this[Cardinal dirn] {
get { return this[(int)dirn]; }
set { this[(int)dirn] = value; }
}
public Vector2Int this[int dirn] {
get { return Neighbours[dirn]; }
set { Neighbours[dirn] = value; }
}
}
Example use:
var cell = new GridNeighbours();
var NorthNeighbour = cell[Cardinal.North];
Assert(cell[Cardinal.South] == cell[2]); // This is true!
===
Alternatively, if you want "direct" properties:
public struct GridNeighbours {
public enum Cardinal { North, East, South, West }
public static Vector2Int[] Neighbours = { new Vector2Int(0, 1), new Vector2Int(1, 0), new Vector2Int(0, -1), new Vector2Int(-1, 0) };
public static Vector2Int North { get { return Neighbours[0]; } set { Neighbours[0] = value; } }
public static Vector2Int East { get { return Neighbours[1]; } set { Neighbours[1] = value; } }
public static Vector2Int South { get { return Neighbours[2]; } set { Neighbours[2] = value; } }
public static Vector2Int West { get { return Neighbours[3]; } set { Neighbours[3] = value; } }
}

Without using reflection I don't think you can do it given the signature you have there.
What you could do is to setup an indexer on the struct that would allow you to loop.

If you mean the possible values in the struct will not be known in the compile-time, then you can try using Reflection. More specifically, using Type.GetFields() or Type.GetProperties().
Example:
Type structType = typeof(GridNeighbours);
FieldInfo[] fields = structType.GetFields();
Foreach(FieldInfo field in fields)
{
//Do something
}
But keeping the fact in mind that there will always be four directions, why a simple GridNeighboursInstance.North is not a preference for you.

If you don't want to use a dynamic way so I can suggest you to use a static way! When you have only those four members and you want to iterate over them you need to implement an AsEnumerable method inside your struct something like this:
public static IEnumerable<Vector2Int> AsEnumerable()
{
for (var i = 0; i < 4; i++)
{
switch (i)
{
case 1:
yield return North;
break;
case 2:
yield return South;
break;
case 3:
yield return East;
break;
case 4:
yield return West;
break;
}
}
}
I think there are some better way, But HTH ;).

Related

Using getter/setter for an array

I'm trying to create a getter/setter for an array but I'm not sure how to write it. The class will have multiple properties that will need to be modified and accessed from another class. Here is an idea of what I'm trying to do:
class MyArrayClass {
private double[] myArray = {1.1, 2.2};
public double MyArray {
get { return myArray[index]; }
set { myArray[index] = value; }
}
}
class AnotherClass {
MyArrayClass mAC = new MyArrayClass();
mAC.MyArray[1] = 3.3;
}
Now that code doesn't work but I hope it expresses what I'm trying to do. I was able to achieve what I wanted using the below code (which does work) however it only works for one property within that class.
class MyArrayClass {
private double[] myArray = {1.1, 2.2};
public double this[int index] {
get { return myArray[index]; }
set { myArray[index] = value; }
}
}
class AnotherClass {
MyArrayClass mAC = new MyArrayClass();
mAC[1] = 3.3;
}
I'm also not sure how to define the values of different index positions without doing it multiple times, e.g.
mAC.MyArray[0] = 1.1;
mAC.MyArray[1] = 2.2;
As opposed to something such as:
mAC.MyArray[0, 1] = {1.1, 2.2};
Sorry if it's a mess but I hope it conveys what I'm trying to achieve.
Since you have several arrays, you need to expose several array-like indexable properties. Using your second MyArrayClass as an example, you can do it like this:
class MyTwoArrays {
private MyArrayClass array1 = ...;
private MyArrayClass array2 = ...;
public MyArrayClass Array1 {
get { return array1; }
}
public MyArrayClass Array2 {
get { return array2; }
}
}
Now you can use it like this:
MyTwoArrays two = new MyTwoArrays();
two.Array1[0] = 123.456;
two.Array2[0] = 789.432;
mAC.MyArray[0, 1] = {1.1, 2.2};
This is not the way it should be, it has to be done one by one. The way you're using would change it Two dimensional array, (somehow, it would be invalid, zero main elements and 1 element in each).
mAC.MyArray[0] = 1.1;
mAC.MyArray[1] = 2.2;
Is the valid way to set the arrays.
class MyArrayClass {
private double[] myArray = {1.1, 2.2};
public double this[int index] {
get { return myArray[index]; }
set { myArray[index] = value; }
}
}
class AnotherClass {
MyArrayClass mAC = new MyArrayClass();
mAC[1] = 3.3;
}
Code works because if the valid way of doing the process because only in this case, you're passing an actual index number to the array to return the value from. In the first code block, there was no Index number, and the return was an Array object.
If I understand correctly, you want your indexer to access 2 internal arrays representing heights and weights of animals.
class MyArrayClass {
private double[] heights = {1.1, 2.2};
private double[] weights = {1.1, 2.2};
public double[] this[int index] { //Or Tuple<double ,double>
get { return new double[] { heights[index], weights[index] }; }
set { heights[index] = value[0]; weights[index] = value[1]; }
}
}
If your intention is to access 2 numbers from 1 array, you can do that too.
class MyArrayClass {
private double[] myArray = {1.1, 2.2, 3.3, 4.4};
public double[] this[int index] {
get { return new double[] { myArray[index*2], myArray[index*2+1] }; }
set { myArray[index*2] = value[0]; myArray[index*2+1] = value[1]; }
}
}
This doesn't include checking the index or the argument (it can be null or have length < 2).
It might be more convenient in this case to have an Animal struct or something:
class MyArrayClass {
private Animal[] animals = {new Animal(1.1, 1.1), new Animal(2.2, 2.2)}
public Animal this[int index] {
get { return animals[index]; }
set { animals[index] = value; }
}
}

C# enum in public variable .NET

For my program, I created a new class called FinishedPiece with a number of public variables available to my main program. For example:
class FinishedPiece
{
private double _PieceLength;
public double PieceLength
{
get { return _PieceLength; }
set { _PieceLength = value; }
}
}
This all works fine, because then I can declare a new FinishedPiece and add properties:
FinishedPiece piece = new FinishedPiece();
piece.PieceLength = 48.25;
My question is, how do the same with an enum? If I do
public enum Cut
{
Angle = 0,
Straight = 1,
AngleThenStraight = 2,
StraightThenAngle = 3
};
then I'd like to change it something like this: piece.Cut = Cut.Angle; but I can only change it by declaring a new FinishedPiece.Cut object:
FinishedPiece.Cut cut = new FinishedPiece.Cut();
cut = FinishedPiece.Cut.Angle;
How do I make an enum available inside a variable so I can do piece.Cut = Cut.Angle? To me it would make sense to do something like this, but it doesn't appear to work.
public int Cut
{
get { return _Cut; }
set { _Cut = value; }
}
private enum _Cut
{
Angle = 0,
Straight = 1,
AngleThenStraight = 2,
StraightThenAngle = 3
};
Thanks in advance! Let me know if my question is unclear and I'll try to help as best as I can.
How do I make an enum available inside a variable so I can do
piece.Cut = Cut.Angle?
Just define another property of type Cut in your class like:
public Cut Cut { get; set; }
Then you can do:
FinishedPiece piece = new FinishedPiece();
piece.PieceLength = 48.25;
piece.Cut = Cut.Angle; //like this
So your class would like like:
class FinishedPiece
{
private double _PieceLength;
public double PieceLength
{
get { return _PieceLength; }
set { _PieceLength = value; }
}
public Cut Cut { get; set; }
}
Consider using Auto-Implemented properties, if you have only simple set and get
Like this:
class FinishedPiece
{
private double _PieceLength;
public double PieceLength
{
get { return _PieceLength; }
set { _PieceLength = value; }
}
private Cut _Cut;
public Cut Cut
{
get { return _Cut; }
set { _Cut = value; }
}
}
public enum Cut
{
Angle = 0,
Straight = 1,
AngleThenStraight = 2,
StraightThenAngle = 3
};
Then you can do:
var piece = new FinishedPiece();
piece.Cut = Cut.AngleThenStraight;
You can try this:
private enum Cut
{
//if you dont want to use any of these values as defaults
//just add another value and in your class private member
//assign it like for example a value called None
Angle = 0,
Straight = 1,
AngleThenStraight = 2,
StraightThenAngle = 3
};
public class FinishedPiece
{
//give it a default,if like stated in the enum you dont want
//any of those values create a None and place it here as default.
private Cut cutObj = Cut.Angle;
public Cut CutObj
{
get { return cutObj; }
set { cutObj = value; }
}
}
Then in your calling code...
FinishedPiece piece = new FinishedPiece();
//if you dont want the default change it...
piece.CutObj = Cut.Straight;

how can i get an access to the elements of array in the class

I have a problem which I don't know how to solve. I have a class. This class has two arrays. I would like to get access via properties. How can I do it? I tried to use indexers, but it is possible if I have only one array. Here what I want to do:
public class pointCollection
{
string[] myX;
double[] myY;
int maxArray;
int i;
public pointCollection(int maxArray)
{
this.maxArray = maxArray;
this.myX = new string[maxArray];
this.myY = new double[maxArray];
}
public string X //It is just simple variable
{
set { this.myX[i] = value; }
get { return this.myX[i]; }
}
public double Y //it's too
{
set { this.myY[i] = value; }
get { return this.myY[i]; }
}
}
With this code, my X and Y are only simple variables, but not arrays.
If I use indexers, I get access only to one array:
public string this[int i]
{
set { this.myX[i] = value; }
get { return this.myX[i]; }
}
But how can I get access to second array?
Or I can't use property in this case? And I need only use:
public string[] myX;
public double[] myY;
An example with Tuples.
public class pointCollection
{
Tuple<String,Double>[] myPoints;
int maxArray;
int i;
public pointCollection(int maxArray)
{
this.maxArray = maxArray;
this.myPoints = new Tuple<String,Double>[maxArray];
}
public Tuple<String,Double> this[int i]
{
set { this.myPoints[i] = value; }
get { return this.myPoints[i]; }
}
}
And to access the points you do...
pointCollection pc = new pointCollection(10);
// add some data
String x = pc[4].Item1; // the first entry in a tuple is accessed via the Item1 property
Double y = pc[4].Item2; // the second entry in a tuple is accessed via the Item2 property
If I got it right, you need some kind or read/write-only wrapper for arrays to be exposed as properties.
public class ReadWriteOnlyArray<T>{
private T[] _array;
public ReadWriteOnlyArray(T[] array){
this._array = array;
}
public T this[int i]{
get { return _array[i]; }
set { _array[i] = value; }
}
}
public class pointCollection
{
string[] myX;
double[] myY;
int maxArray;
public ReadWriteOnlyArray<string> X {get; private set;}
public ReadWriteOnlyArray<double> Y {get; private set;}
public pointCollection(int maxArray)
{
this.maxArray = maxArray;
this.myX = new string[maxArray];
this.myY = new double[maxArray];
X = new ReadWriteOnlyArray<string>(myX);
Y = new ReadWriteOnlyArray<double>(myY);
}
}
and usage
var c = new pointCollection(100);
c.X[10] = "hello world";
c.Y[20] = c.Y[30] + c.Y[40];
The closest you'll come without either changing your data structure or moving to methods is to make a property that returns each array, much like you did in your first code block, except without the [i].
Then, you do var x = instanceOfPointCollection.MyX[someI]; for example.

Use Class property value for a switch statement

am using generated protobuf code (see http://code.google.com/p/protobuf/)
I have a generated class looks like this :
public class Fruits{
private int _ID_BANANA = (int)1;
private int _ID_APPLE = (int)2;
public int ID_BANANA
{
get { return _ID_BANANA; }
set { _ID_BANANA = value; }
}
public int ID_APPLE
{
get { return _ID_APPLE; }
set { _ID_APPLE = value; }
}
}
Then they are constant values but I can't use then as such in my code.
For example I want to do a mapper like this :
public static Color GetColor(int idFruit) {
switch (idFruit)
{
case new Fruits().ID_BANANA:
return Color.Yellow;
case new Fruits().ID_APPLE:
return Color.Green;
default:
return Color.White;
}
}
I have the error : a constant value is expected.
I thougth about creating an enum, but seems to be the wrong way, tryed something like :
public const int AppleId = new Fruits().ID_APPLE;
Not working either...
Someone have an idea?
Why not to use if else if statements here?
var fruits = new Fruits();
if (idFruit == fruits._ID_BANANA)
return Color.Yellow;
else if (idFruit == fruits._ID_APPLE)
return Color.Green;
else
return Color.White;
Or dictionary:
this.fruitsColorMap = new Dictionary<int, Color>
{
{ fruits._ID_BANANA, Color },
{ fruits._ID_APPLE, Green }
};
And then:
public static Color GetColor(int idFruit) {
if (this.fruitsColorMap.ContainsKey(idFruit) {
return this.fruitsColorMap[idFruit];
}
return Color.White;
}
The case values in a switch statement have to be constants - that's part of the switch statement's definition:
switch (expression)
{
case constant-expression:
statement
jump-statement
[default:
statement
jump-statement]
}
as per the switch (C#) documentation from Microsoft.
you will notice the constant-expression bit. That means that it can be determined at compile time. So calls to functions (and a reference to a property property is really a function call in disguise) are not allowed here.

Why do I get NullReferenceException here?

I have:
partial class StarSystem : AstroThreeNode
{
public static StarSystem SunSolarSystem()
{
return new StarSystem("Solar System", Planet.SunCenter(), Resources.Space, SolarSystem.ActiveForm.Bounds).AddPlanet(Planet.MercuryPlanet(), Planet.VenusPlanet(), Planet.EarthPlanet(), Planet.MarsPlanet(), Planet.JupiterPlanet(), Planet.SaturnPlanet(), Planet.UranusPlanet(), Planet.NeptunePlanet());
}
public StarSystem AddPlanet(params Planet[] planetsToAdd)
{
foreach (Planet planet in planetsToAdd)
{
distanceFromSun += (planets.Count > 0 ? planets[planets.Count - 1].Image.Width / 2 : 0) + planet.Image.Width / 2 + DISTANCE_BETWEEN_PLANETS;
planet.DistanceFromSun = distanceFromSun;
planet.RotationCenter = new PointF(bounds.Width / 2, bounds.Height / 2);
planets.Add(planet);
}
return this;
}
}
partial class Planet : AstroThreeNode
{
private const float SUN_SPEED = 0;
public static Planet SunCenter()
{
return new Planet("Слънце", Resources.Sun_, SUN_SPEED, CLOCKWISE);
}
public static Planet MercuryPlanet()
{
return new Planet("Меркурий", Resources.Mercury_, 4.0923f, Planet.CLOCKWISE);
}
public static Planet VenusPlanet()
{
return new Planet("Венера", Resources.Venus_, 1.6021f, Planet.COUNTERCLOCKWISE);
}
public static Planet EarthPlanet()
{
return new Planet("Земя", Resources.Earth_, 0.9856f, Planet.CLOCKWISE);
}
public static Planet MarsPlanet()
{
return new Planet("Марс", Resources.Mars_, 0.5240f, Planet.CLOCKWISE);
}
public static Planet JupiterPlanet()
{
return new Planet("Юпитер", Resources.Jupiter_, 0.0830f, Planet.CLOCKWISE);
}
public static Planet SaturnPlanet()
{
return new Planet("Сатурн", Resources.Saturn_, 0.0334f, Planet.CLOCKWISE);
}
public static Planet UranusPlanet()
{
return new Planet("Уран", Resources.Uranus_, 0.0117f, Planet.COUNTERCLOCKWISE);
}
public static Planet NeptunePlanet()
{
return new Planet("Нептун", Resources.Neptune_, 0.0059f, Planet.CLOCKWISE);
}
public Planet(string name, Image image, float distanceFromSun, float degreesAddedEachTick, bool clockwise, PointF rotationCenter, float angleInDegrees = 0)
{
this.Name = name;
this.Image = image; // here I set the Image but I get an exception later
this.DistanceFromSun = distanceFromSun;
this.degreesAddedEachTick = degreesAddedEachTick;
this.isClockwiseRotation = clockwise;
this.RotationCenter = rotationCenter;
this.angleInDegrees = angleInDegrees;
}
}
I get the exception here: distanceFromSun += (planets.Count > 0 ? planets[planets.Count - 1].Image.Width / 2 : 0) + planet.Image.Width / 2 + DISTANCE_BETWEEN_PLANETS;
For the planet.Image. I don't get it why do I get exception when I set the Image when I create a Planet.
EDIT: Here is the constructor for StarSystem :
public StarSystem(string name, Planet starSystemCenter, Image background, Rectangle bounds)
{
this.Name = name;
this.Background = background;
this.bounds = bounds;
planets = new List<Planet>(); //planets instanieted here
planets.Add(starSystemCenter); // note I dont get the exception on this call but on the other call where I add all other planest
}
EDIT 2:
Found my problem but I have to go to bed I will post the answer tomorrow.
It looks like you're never instantiating your 'Planets' object (probably a List<Planet>?)
Is it in your StarSystem code?
Presumably it would be something like
public class StarSystem
{
public List<Planet> planets {get; set;}
//Other code here
public StarSystem()
{
planets = new List<Planet>();
}
}
But that code is not in evidence in your post. That's what I find to be the most likely culprit.
Post Question Edit:
Okay, then the only thing that seems to leave is the .Image for one or more of your planets. When you debug, you should be able to watch your variables- I would set a break-point before it evaluates that expression, and look at the last object it added, and the next object it's going to try to add, and look for a null .Image
Try to have class members, local variables, and method parameters differ by more than capitalization. See Bad Naming Convention in the canonical NullReferenceException post linked above.
You create each planet with this constructor
public Planet(string name, Image image, float distanceFromSun, float degreesAddedEachTick, bool clockwise, PointF rotationCenter, float angleInDegrees = 0)
Yet you never check to make sure that image != null && image.Width != null (if width is a native int you don't need to, but if it is a Number you do need that check.

Categories