Array and List discrepencies with structs - c#

In the following code, a struct is obtained from an array and from a list. When getting the item by index, the array appears to do it by reference whereas the list appears to do it by value. Can someone explain the reasoning behind this?
struct FloatPoint {
public FloatPoint (float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public float x, y, z;
}
class Test {
public static int Main (string[] args) {
FloatPoint[] points1 = { new FloatPoint(1, 2, 3) };
var points2 = new System.Collections.Generic.List<FloatPoint>();
points2.Add(new FloatPoint(1, 2, 3));
points1[0].x = 0; // not an error
points2[0].x = 0; // compile error
return 0;
}
}
Changing the struct definition to a class makes both compile.

When you get a struct, it is always by value. The structure will be copied, you don't get a reference to it.
The difference is that you can access the sctruct directly in the array, but not in the list. When you change the property in the struct in the array, you access the property directly, but to do the same with a list you have to get the struct, set the property, then store the struct back in the list:
FloatPoint f = points2[0];
f.x = 0;
points2[0] = f;
Earlier versions of the compiler would let you write the code that you have, but for a list it would generate code similar to this:
FloatPoint f = points2[0];
f.x = 0;
I.e. it would read the struct, change it, and silently throw the changed struct away. The compiler was changed to give an error in that case.

Related

How do i create a struct in C# that contains an array but does not use heap?

What i need:
a polygon with arbitrary amount of vertices ( or at least up to max number of vertices )
it should be a struct, so that it can be fast and can be assigned / passed by value
It seems like i can't use arrays or collections for storing vertices, because then my polygon struct would point to objects on a heap, and when one polygon is assigned to another one by value only shallow copy would be performed, and i would have both polygons pointing to the same vertex array. For example:
Polygon a = new Polygon();
Polygon b = a;
// both polygons would be changed
b.vertices[0] = 5;
Then how do i create a struct that can have arbitrary number (or some fixed number) of vertices, but without using heap at all?
I could just use lots of variables like v1, v2, v3 ... v10 etc, but i want to keep my code clean, more or less.
You have the option to define your array with the fixed keyword, which puts it in the stack.
But you cannot directly access the elements of the array, unless you are in an unsafe context and use pointers.
To get the following behavior:
static void Main(string[] args)
{
FixedArray vertices = new FixedArray(10);
vertices[0] = 4;
FixedArray copy = vertices;
copy[0] = 8;
Debug.WriteLine(vertices[0]);
// 4
Debug.WriteLine(copy[0]);
// 8
}
Then use the following class definition:
public unsafe struct FixedArray
{
public const int MaxSize = 100;
readonly int size;
fixed double data[MaxSize];
public FixedArray(int size) : this(new double[size])
{ }
public FixedArray(double[] values)
{
this.size = Math.Min(values.Length, MaxSize);
for (int i = 0; i < size; i++)
{
data[i] = values[i];
}
}
public double this[int index]
{
get
{
if (index>=0 && index<size)
{
return data[index];
}
return 0;
}
set
{
if (index>=0 && index<size)
{
data[index] = value;
}
}
}
public double[] ToArray()
{
var array = new double[size];
for (int i = 0; i < size; i++)
{
array[i] = data[i];
}
return array;
}
}
A couple of things to consider. The above needs to be compiled with the unsafe option. Also the MaxSize but be a constant, and the storage required cannot exceed this value. I am using an indexer this[int] to access the elements (instead of a field) and also have a method to convert to a native array with ToArray(). The constructor can also take a native array, or it will use an empty array to initialize the values. This is to ensure that new FixedArray(10) for example will have initialized at least 10 values in the fixed array (instead of being undefined as it is the default).
Read more about this usage of fixed from Microsoft or search for C# Fixed Size Buffers.
Heap array field
struct StdArray
{
int[] vertices;
Foo(int size)
{
vertices = new int[size];
}
}
Stack array field
unsafe struct FixedArray
{
fixed int vertices[100];
int size;
Foo(int size)
{
this.size = size;
// no initialization needed for `vertices`
}
}
If it suits your logic, you could use a Span<T>, which is allocated on the stack. Read more here
One other way to just copy the array with a copy constructor
public Polygon(Polygon other)
{
this.vertices = other.vertices.Clone() as int[];
}
then
var a = new Polygon();
a.vertices[0] = 5;
var b = new Polygon(a):
Debug.WriteLine(a.vertices[0]);
// 5
Debug.WriteLine(b.vertices[0]);
// 5
b.vertices[0] = 10;
Debug.WriteLine(a.vertices[0]);
// 5
Debug.WriteLine(b.vertices[0]);
// 10

Value Tuple initialization is counter-intuitive

In the Sunday evening I have been watching some conferences this time I get hands on this one Conference Link
Where I found out pretty interesting think there is simple code example:
struct Point
{
private double x;
public double X { get => x; set => x = value; }
private double y;
public double Y { get => y; set => y = value; }
public Point(double x, double y) => (this.x, this.y) = (x, y);
public void SwapCode() => (X, Y) = (Y, X);
}
In Main:
var point = new Point(10.0, 11.0);
Console.WriteLine($"x: {point.X}, y: {point.Y}");
point.SwapCode();
Console.WriteLine($"x: {point.X}, y: {point.Y}");
And there is output of this :
x: 10, y: 11
x: 11, y: 10
So there is some questions:
How does it works ?
What I mean by that is Tuples should be translate into Tuple<T, K> which should be initialize with copy of the values but there it assign values to the variables at least for me it's kind of counter intuitive.
And i wonder if it's just sugar syntax think or there is happen some magic under this what make perfect sense but I can't spot it out?
Firstly structs should be immutable. Even though you can do this, you probably shouldn't.
Secondly, your SwapCode is actually doing this, as seen here.
public void SwapCode()
{
double num = Y;
double num2 = X;
double num4 = X = num;
num4 = (Y = num2);
}
Yeah, it's a little strange. However, it's just a little syntactic magic introduced in C#7. What it is actually doing is using a deconstruct method (the terminology .Net uses) to provide a set of out arguments for each of the params you want to extract. In this case, it's the properties/field you supplied!
To see it a little clearer, consider these two functionally equivalent code blocks
(int x, int y) asd = (1, 2); // create a Value Tuple
(int x, int y) = asd; // deconstruct it
(x, y) = (x, y); // assign to the deconstructed type
// All the above now has the swapped values
// they are all pointing to the same variables/memory
// You could even take this further by
x = 10;
y = 11;
// Once again, x and y, asd, and (x, y) all have the same values
// Because they are the same
// ----------------------------------------------------
int x = 1;
int y = 2;
(x, y) = (y, x); // we are just deconstructing our original variables
// All the above now has the swapped values
// they are all pointing to the same variables/memory
Note : As you can see, this is also a slightly more succinct way of swapping 2 variables as you don't have to use a temp variables, your friendly CLR does it for you
Anyway, you shouldn't be doing this with a struct anyway, they really should be immutable for various reasons

Why didn't this line of code update the object? [duplicate]

This question already has answers here:
What do ref, val and out mean on method parameters?
(4 answers)
Closed 6 years ago.
Given that there is a class called Point with a float x and float y component.
class Point
{
public double x;
public double y;
public Point(double x, double y)
{
this.x = x;
this.y = y;
}
public Point()
{
x = y = 0;
}
public override string ToString()
{
return string.Format("({0:F2},{1:F2})", x, y);
}
}
Why in the mystery1 function didn't the point p1 get updated to be (11.00, 11.00) since p.x is 11 from the first line?
{
Point p1 = new Point(11, 22);
Point p2 = p1;
int n1 = 33;
int n2 = n1;
mystery1(p1, n1);
Console.WriteLine("n1 = {0}, n2 = {1}", n1, n2);
Console.WriteLine("p1 = {0}, p2 = {1}", p1, p2);
mystery2(p2, n2);
Console.WriteLine("n1 = {0}, n2 = {1}", n1, n2);
Console.WriteLine("p1 = {0}, p2 = {1}", p1, p2);
}
static void mystery1(Point p, int n)
{
n = (int)p.x;
p = new Point(n, n);
}
static void mystery2(Point p, int n)
{
p.x = 77;
n = 88;
}
Point is a value type. so when you call a method with a point as parameter, you have a new instance of the point in the method. So you are changing the values not on that instance that you have in the main method.
Have a look at the ref keyword: https://msdn.microsoft.com/en-us/library/0f66670z.aspx
Also at the moment you make a new instance with new Point(n, n); you create even one more instance of point .
You main method still points to the old object which is not getting changed.
Also I would recommend that you have a general look at the differences between value and complex types like here: https://msdn.microsoft.com/en-us/library/34yytbws(v=vs.100).aspx
or here:
https://msdn.microsoft.com/en-us/library/s1ax56ch.aspx
In C# all paramaters are passed by value, even references.
That means when you pass an instance of a class to a method you pass the reference of that instance as value. Imagine the reference (similar to a pointer) is simply the adress of an instance within the stack stored into a string (it´s not a string, but for simplicity let´s assume so). Now when you change that string what should happen to the outside? Nothing, because the string is just a value, it does not have anything to do with your actual referenced instance. This sounds weird, but that´s it. In your msyter1-method you simply re-assign this reference to a new instance, however you simply change the value of that reference, not the reference itself.
You could do this by passing the class by reference`:
static void mystery1(ref Point p, ref int n)
Now changes within the method are reflected to the outer of the method.
In mystery2 there´s a difference and you won´t need the ref-keyword for your Point-class, because you´re not changing the reference itself but a property of the referenced instance. However this does not apply to the integer-argument which is of course a value-type and thus must be passed by ref to be updated outside your method also:
static void mystery2(Point p, ref int n)

Unity 5.3 - C# -- List<Vector2> How to extract biggest X-value?

I'm developing a C# script on Unity 5.3. I have a list of Vector2 values and I need to extract the biggest X value in the list. I'm trying to do the following:
public List<Vector2> Series1Data;
... //I populate the List with some coordinates
MaXValue = Mathf.Max(Series1Data[0]);
However, I get the following errors:
error CS1502: The best overloaded method match for `UnityEngine.Mathf.Max(params float[])' has some invalid arguments
error CS1503: Argument `#1' cannot convert `UnityEngine.Vector2' expression to type `float[]'
Is there any other way of extracting the biggest X value in the list?
You are trying to put a List on a function that can't have that type of variable as parameter.
Mathf.Max here you can see which type of parameters it can handle.
This code might do the work:
public List<Vector2> Series1Data;
... //I populate the List with some coordinates
MaXValue = Series1Data[0].x; //Get first value
for(int i = 1; i < Series1Data.Count; i++) { //Go throught all entries
MaXValue = Mathf.Max(Series1Data[i].x, MaXValue); //Always get the maximum value
}
You can do this via Linq:
MaxXValue = Series1Data.Max(v => v.x);
This assumes you Series1Data List object is not null or empty.
You probably could try like this:
float xMax = Single.MinValue;
foreach (Vector2 vector in Series1Data)
{
if (vector.X > xMax)
{
xMax = vector.X;
}
}
Your problem lies within the fact that you are passing a List<> of type Vector2 when Mathf.Max() takes an array of type float. Instead of your code, do something like this:
public Vector2[] Series1Data;
public float[] Series1DataX;
... //Populate Series1Data with data like this: Series1Data[number] = data;
... //Populate Series1DataX with data like this: Series1DataX[number] = data.x;
MaXValue = Mathf.Max(Series1DataX);
So you have two arrays: an array that stores all the Vector2s and one that stores the Vector2s' X values.
Use like below.
MaXValue = Mathf.Max(Series1Data[0].x);
Cause of your error, Series1Data[] is vector2 and MaxValue is float. It's a type conversion errors. And you want x value from Series1Data[].
For getting the max value from the list you have to iterate through all x values and check the larger one with next one and replace current max if it's larger.
Or you can sort the list ascending and get the first index values as max.
You are attempting to get the maximum Vector itself with your code, instead you want to get the maximum 'x' value of the vector - which is a float instead of a vector; try this code:
public List<Vector2> Series1Data;
//I populate the List with some coordinates
MaXValue = Mathf.Max(Series1Data[0].x);
EDIT:
I just realised that the Mathf.Max() function takes an array of floats as a parameter - you'll need to separate the 'x' value floats from the vector; perhaps the following may work:
public List<Vector2> Series1Data;
//I populate the List with some coordinates
float[] xCoordinates = new float[Series1Data.Count]
for(int i = 0; i < Series1Data.Count; i++)
{
xCoordinates[i] = Series1Data[i].x;
}
MaXValue = Mathf.Max(xCoordinates);
Unity's Mathf.Max only works with float arrays,
you can make a temporary array with all floats to solve it
using UnityEngine;
using System.Collections.Generic;
public class LelExample : MonoBehaviour
{
public List<Vector2> Vec2List = new List<Vector2>();
public float maxX;
public void Start()
{
float[] x; //set temp arrays
float[] y;
GetArraysFromVecList(Vec2List, out x, out y); //set the arrays outputting x and y
maxX = Mathf.Max(x); //Max the x's
}
public void GetArraysFromVecList(List<Vector2> list, out float[] x, out float[] y) //My Custom Void
{
float[] tx = new float[list.Count]; //Define temporary arrays
float[] ty = new float[list.Count];
for (int i = 0; i < list.Count; ++i)
{
tx[i] = list[i].x; //Add x to each corrosponding tx
ty[i] = list[i].y; //Add y to each corrosponding ty
}
x = tx; //set the arrays
y = ty;
}
}
Hope this works :) ~Sid

multiple out parameters with a return

I have a method declaration like this:
public int myMethod(int x, out int y, out int z)
{
int k;
foreach(int i in someList)
{
if(anotherMethod(out k))
{
z = k;
}
else
{
z = 0;
}
}
y = someValue;
return anotherValue;
}
but I get this compiling error
The out parameter 'z' must be assigned to before control leaves the current method
If someList is empty, it will never enter the foreach loop, and therefore z will never be assigned. To resolve this, ensure that z is given a value regardless of of the contents of someList:
public int myMethod(int x, out int y, out int z)
{
z = 0; // or whatever default value you like
...
}
However, you should probably consider refactoring this code. It's likely there's a better way to accomplish this. If you'd really like to return 3 different int values, you might consider using a Tuple<int, int, int> or creating a custom data type to represent the value.
Reason : out paramaeters must be initialised before returning from the function.
You are assigning value for parameter z inside if block so compiler
could not identify whether it can be initialized or not hence initialize
your parameter z before if block as below:
public int myMethod(int x, out int y, out int z)
{
int k;
z=0;
foreach(int i in someList)
{
if(anotherMethod(out k))
{
z = k;
}
else
{
z = 0;
}
}
y = someValue;
return anotherValue;
}
If someList is empty, then z will never be assigned a value, which violates it's being an out variable. Remove the out constraint, or reconfigure your function logic.

Categories

Resources