I wanted to try c#'s unsafe 'feature' by creating simple structs (Vector, Particle).
SITUATION
I have this 2 structs and want to inject position and velocity vectors into my Particle struct.
As a test I wanted to print out position's X value, but somehow I'm getting random values.
I have the following code here
Vector
public readonly struct Vector
{
public int X { get; }
public int Y { get; }
public Vector(int x, int y)
{
X = x;
Y = y;
}
}
Particle
public unsafe struct Particle
{
private Vector* mPosition;
private Vector* mVelocity;
public Particle(Vector position, Vector velocity = default)
{
mPosition = &position; // here is x 10
mVelocity = &velocity;
}
public int GetPosX()
{
return mPosition->X; // but here not
}
}
Program
public class Program
{
private static void Main(string[] args)
{
var pos = new Vector(10, 5);
var obj = new Particle(pos);
Console.WriteLine(obj.GetPosX()); // prints random value
}
}
PROBLEM
It prints a random value instead of 10.
class Program {
static void Main (string [ ] args) {
unsafe {
Vector pos = new Vector(10, 5);
Particle obj = new Particle(&pos);
// &pos is at position 0xabcdef00 here.
// obj.mPosition has a different value here. It points to a different address? Or am I misunderstanding something
Console.WriteLine(obj.GetPosX( ));
}
}
}
public struct Vector {
public int X;
public int Y;
public Vector (int x, int y) {
X = x;
Y = y;
}
}
public unsafe struct Particle {
private Vector* mPosition;
public Particle (Vector *position) {
mPosition = position; // here is x 10
}
public int GetPosX ( ) {
return mPosition->X; // still 10 here
}
}
This works for me.
Please ... do not ask me why it does. You will notice that I didn't change that much. Just calling Particle with *pos instead of pos. For some reason that fixes the problem. You have to wrap the code with unsafe then and change the constructor for Particle obviously.
I could speculate about why it works, but I'd rather not. Maybe the pointer changes when you pass pos as a parameter for some reason?
You could not take the ref with right value.
Create a variable like int posX = 10;
And you can take the reference with variable. You take the compile time reference and read the runtime reference.
Don't use pointers without fixed. C# stack performance ise very good. You dont need this.
Usually pointers use with linking (C/Cpp dynamic library linking etc). If you have large structs (30 bytes and greater) then you can use the ref parameter tag.
Related
I'm trying to cut down on how much duplication I have on my code, so I decided to make one of my classes a static class since I decided that its data should really be shared with everyone. Here's the static method below:
// A static class, that holds all object's coordinates, and methods to return & update their values.
internal static class Coordinate
{
private static int[,] PlayerCoordinate { get; set; }
public static int[,] GateCoordinate { get; }
public static int[,] FountainCoordinate { get; }
static Coordinate() // FIRST VALUE IS X (column), SECOND VALUE IS Y (row).
{
PlayerCoordinate = new int[,] { { 0, 0 } };
GateCoordinate = PlayerCoordinate; // Just starts off in the same place as player.
FountainCoordinate = new int[,] { { 2, 0 } };
}
// A static method, that sends the data of all object coordinates, deconstructed into seperate ints.
public static int PlayerColumn() { return PlayerCoordinate[0, 0]; }
public static int PlayerRow() { return PlayerCoordinate[0, 1]; }
public static int GateColumn() { return GateCoordinate[0, 0]; }
public static int GateRow() { return GateCoordinate[0, 1]; }
public static int FountainColumn() { return FountainCoordinate[0, 0]; }
public static int FountainRow() { return FountainCoordinate[0, 1]; }
// Updates the coordinates of the player.
public static void UpdatePlayerCoordinate(int column, int row) { PlayerCoordinate = new int[,] { { column, row } }; }
}
The main issue comes in from my GameManager class. On the console, the beginning section should print out "You are the room at (Column=0, Row=0), but it prints this instead:
Here is the code for my GameManager class:
internal class GameManager
{
private bool IsGameOver;
private Player Player;
private Updater Updater;
// Don't need to call Fountain or Coordinate since they're static
public GameManager()
{
IsGameOver = false;
Player = new();
Updater = new();
}
public void RunGame()
{
while (!IsGameOver)
{
Console.WriteLine("----------------------------------------------------------");
Updater.DisplayPlayerPosition(); // This is the main line that I'm having issues with as of right now. All other functions past this are another problem.
Updater.DisplayPlayerSenses();
string playerInput = Player.GetInput();
Updater.MovePlayer(playerInput);
IsGameOver = Updater.CheckForWin();
}
}
}
And just to make sure, here is the code from my updater class, with the specific method that I'm having issues with:
internal class Updater
{
// No fields
// Default constructor
// Gets the text to show the player his current position.
public void DisplayPlayerPosition() // This is the method that I'm having issues with.
{
Console.WriteLine($"You are in the room at (Column={Coordinate.PlayerColumn}, Row={Coordinate.PlayerRow})");
}
...
I'm fairly new to the static keyword so I believe that I may be missing smth. I personally believe that it's because the class itself hasn't been initialized (like I haven't called the constructor for the Coordinate class, and apparently you can't call a static constructor anyways), but that's just me. If I could get any help, I'd greatly appreciate it!
PlayerColumn() and PlayerRow() are methods, but you are accesing them in the WriteLine statement as if they are properties.
Update your WriteLine to:
Console.WriteLine($"You are in the room at (Column={Coordinate.PlayerColumn()}, Row={Coordinate.PlayerRow()})");
I've been benchmarking some C# struct creation, and I've been surprised to find that creation of a nested struct is significantly slower than the un-nested version. I would have expected the JITter to basically produce the same assembly for a flat and nested structure, but this appears to not be the case.
Does anyone have an explanation for why the struct construction in my test code below doesn't get optimized down to just writing the 3 doubles? It seems to (if I'm reading the assembly correctly) zero and create a temporary memory location before writing the values to the final location - presumably actually creating a temporary copy of the nested struct.
And in the follow-up, is there a way to avoid this extra work such that the nested struct has similar creation speed to the un-nested one?
public class StructBenchmarks
{
public struct Vector3_dp
{
public Vector3_dp(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
public double X;
public double Y;
public double Z;
}
public readonly struct ContainsVector
{
public ContainsVector(double x, double y, double z)
{
m_vector = new Vector3_dp(x, y, z);
}
public ContainsVector(Vector3_dp input)
{
m_vector = input;
}
private readonly Vector3_dp m_vector;
}
private ContainsVector m_vector1;
private ContainsVector m_vector2;
private Vector3_dp m_vector3_dp1;
private Vector3_dp m_vector3_dp2;
// 0.2ns
public void Vector3_dp_Construction()
{
m_vector3_dp1 = new Vector3_dp(20, 30, 40);
}
// 3.8ns
public void Vector_Construction()
{
m_vector1 = new ContainsVector(20, 30, 40);
}
// 3.8ns
public void Vector_ConstructionFromVector3_dp()
{
m_vector1 = new ContainsVector(m_vector3_dp1);
}
// ~0ns
public void Vector3_dp_Copy()
{
m_vector3_dp1 = m_vector3_dp2;
}
// ~0ns
public void Vector_Copy()
{
m_vector1 = m_vector2;
}
}
And SharpLab link: https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBDAzgWwB8BYAKAAJLyABAZhoCZyBlDKAVzAwCEYA7MAAt82KAGtcZKuQDeU6VTrlcbThnIA1GF2i0A+gBMADvIWzTZxfS06o+4wAoDEdsAA2McgjTlnrj+QAnj5+7p4AXgCUFpbmFLGWABrkALxeANwxCZQAmqlBmfHZ0gBa+eGFxeQAvmRZZkqhAYmVxY0uYeQ5rdnt/p4lPQq1pPU09LDYznxugcqqXOQAwhB8GNgAlny4NhjQY3JFCUora5vbu9BOHQHevjeewff95FFj0odV0vh6AG7aeyg+T4MAA7poAbpDEYHHcnlEhrERu8rMtVustjtIVAHJc7NDyFsjOwMNEjrFPl9KD9/rZ8kSSYjLMjyZYjFANr9sBhPJNprMIbZ7EZyDTsUzKCzYuzOdzPKcMRdsaK/tiAIwS8gyrk8tFnTF4lW0wEMQpjbVywWA4VG7HCjXmjk6zx4m1ioXQ011VmUAD0vvIAAYAHQMbZjJTUFBWqHGPSnFQcLgbVYOMkUlHU1Ue4xq4FgmP4xwMQM+Wil8goQORTVSyz+8i0YMADnDPvGNGjePjq0TahTfDTB0zttsebSIPBCvOWNsDhLZYrVZrYzrZgbTdbknbka72J72wWGAHADEoBB8K7oUP25Tsu7AePyJO9YrZ4CHA/Y0Y1Sv22uFF3QthR7IxAhvDN22+bNrWhJ8vyLIwvX/MYI3oKNC1A8D00sO8EgQ+CYOgZCkQsEYgA=
Consider the following code:
public struct Vector2
{
public float X;
public float Y;
}
public class Sprite
{
private Vector2 position;
public ref Vector2 Position => ref position;
private void DoStuffWhenPositionChanges() { /*...code...*/ }
}
The ref return allows me to do the following set operations:
someSprite.Position.X++;
someSprite.Position.Y = 42;
I would like to invoke DoStuffWhenPositionChanges whenever either the X or Y components of the Position struct are set. Note, Vector2 is a library-level struct, and cannot be changed in my code.
I am looking for a solution similar to:
public class Sprite
{
private Vector2 position;
public Vector2 Position
{
get => position; //ref return not possible!
set
{
position = value;
DoStuffWhenPositionChanges();
}
}
private void DoStuffWhenPositionChanges() { /*...code...*/ }
}
...but with a ref return, to avoid having to do the following in the calling code:
someSprite.Position = new Vector2(someSprite.Position.X, someSprite.Position.Y + 1);
I've considered INotifyPropertyChanged, but as I cannot modify the library-level Vector2 struct, I need an alternative solution. I also considered a proxy type, implicitly convertible to Vector2, that would implement INotifyPropertyChanged, but that seems ...cumbersome; perhaps there's a cleaner solution I am not aware of.
Is it possible to know when the underlying value of the ref return property changes, given the above setup?
One way is to create a wrapper for Struct and then implement RaisePropertyChanged method or INotifyPropertyChanged:
public struct Vector2
{
public float X;
public float Y;
}
public class VectorWrapper
{
private Vector2 thing;
public var X
{
get { return thing.X; }
set { thing.X = value; RaisePropertyChanged(SomePropertyName); }
}
public var Y
{
get { return thing.Y; }
set { thing.Y = value; RaisePropertyChanged(SomePropertyName); }
}
}
It's a bit of a hack but this will get the job done.
static void Main(string[] args)
{
var s = new Sprite();
s.DoStuffWhenPositionChanges(s.Position.X++);
}
public struct Vector2
{
public float X;
public float Y;
}
public class Sprite
{
private Vector2 position;
public ref Vector2 Position => ref position;
public void DoStuffWhenPositionChanges(float f = default)
{
}
}
I am creating a crossword puzzle generator and seem to have an issue with a simple variable assignment of co-ordinates in the grid system.
I have a very simple structure to hold discrete coordinate values as seen below. I have stripped encapsulation to make it easier to read.
public struct vec2
{
public int x, y;
public vec2(int x, int y)
{
this.x = x;
this.y = y;
}
}
This Vec2 Structure is maintained inside a class to hold word values
public struct WordClass
{
string svalue;
bool flag;
public vec2 position;
public WordClass(string sarg, bool barg)
{
this.svalue = sarg;
this.flag = barg;
position = new vec2(0,0);
}
public string StringVal
{
get { return svalue; }
}
public bool FlagVal
{
get { return flag; }
}
public void DisableWord()
{
if (this.flipflop == false)
{
this.flipflop = true;
}
}
public void SetPos(int xa, int ya)
{
this.position.x = xa;
this.position.y = ya;
}
}
This should basically maintain a list of permanent words with a flag for usage, and a variable position as the system calculates optimal locations for the word.
I have a dynamic linked list of words
List<WordClass> WordList = new List<WordClass>();
and to change the coordinates of a word in the wordlist
//Arbitrary values
WordList[0].SetPos(Position_X, Position_Y);
Now my issue is that when I try to use the position of the word, no matter what I set it too prior, it maintains a default value of 0, 0. I have been scratching my head while doing other functionality, and it's left me wondering if I'm missing something important.
Problem seems to be related to the fact that vec2 is a ValueObject and you're trying to change it. The problematic lines are concretely those two:
this.position.x = xa;
this.position.y = ya;
Why? Because vec2 is a a struct each time you read it you get a temporary copy, then modify that copy, then the copy is thrown away, while you're still reading the original, unmodified one. That's one reason why value objects should be immutable as much as possible, unless you've got a strong reason.
The first step should be to make a proper immutable vec2 structure:
public struct vec2
{
public int x { get; private set; }
public int y { get; private set; }
public vec2(int x, int y)
{
this.x = x;
this.y = y;
}
}
Once you've got that, you need to take care of the modification in the SetPos method. Since the structure is immutable you can no longer read it, but instead each time you need to change it you'll throw the current instance away and create a new one:
public void SetPos(int xa, int ya)
{
this.position = new vec2(xa, ya);
}
This creates a brand-new structure and assings it to the internal field, containing the new values. As this doesn't really attempts to modify the structure, but instead change the structure for a new one it won't be subject to the same subtle bug.
Ok,
It is not possible to store a struct instance inside a struct of the same type. So can anyone help me find a workaround please?
I need to store a vector3 inside a vector3 like this:
public struct Vector3{
float x,y,z;
Vector3 normalized;
}
Obviously, it creates an endless cycle as one would create a new one that creates a new one and so on...
So how would one do that? I would need my normalized to be a Vector3 since it needs to be recognized as such and cannot be any other naming.
Finally, I know this can be achieved with classes but I would not want.
Thanks
Well, a struct is a value type. Declaring a recursive struct would create an infinitely big struct! A workaround would be to declare it as class instead. But here I would simply declare Normalized as a property.
public struct Vector3 {
public Vector3(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public float X { get; private set; }
public float Y { get; private set; }
public float Z { get; private set; }
public float Length {
get { return (float)Math.Sqrt(X * X + Y * Y + Z * Z); }
}
Vector3 Normalized {
get {
float l = Length;
return new Vector3(X / l, Y / l, Z / l);
}
}
}
You cannot store a struct of type X inside a struct of type X. However, you probably don't want to do this anyway, because in general, structs (and classes, for that matter) should only store the data they need to be complete. Instead, you can have a function that builds and returns the 'normalized' version of the struct:
public struct Vector3
{
float x,y,z;
public Vector3 Normalized
{
get
{
... build the normalized struct and return it ...
}
}
}