In C#, I have a simple 3D vector class.
static void Main(string[] args)
{
Vector3D a, b;
a = new Vector3D(0, 5, 10);
b = new Vector3D(0, 0, 0);
b = a;
a.x = 10;
Console.WriteLine("vector a=" + a.ToString());
Console.WriteLine("vector b=" + b.ToString());
Console.ReadKey();
}
the output is,
vector a= 10, 5, 10
vector b= 10, 5, 10
I assign a before i change a.x to 10. So i was expecting
vector a= 10, 5, 10
vector b= 0, 5, 10
From what i understand = operator assigns a reference to object like a pointer?
And in C# i cant overload = operator.
Do i have to manually assign each property?
Yes, because Vecor3D is a class this is quite correct.
Classes are reference types and your b = a; statement does not copy a Vector3D instance but a reference to an instance.
If you want to 'clone' the instances, you could add the IClonable interface, but that is more or less abandoned.
A better solution for an <X,Y,Z> type might be to make it a struct. Structs are values types and the meaning of b = a; would change (towards what you want).
A 3D point meets all the criteria for a struct (small, no identity). The preferred way is to design it as immutable.
Yes, "= operator assigns a reference to object like a pointer", as you put it. Thus, both a and b reference the same single object in memory. (The object previously referenced by b is not referenced any more and will be garbage collected.)
There are multiple ways to overcome this problem:
Make Vector3D a struct instead of a class. Structs are value types instead of reference types, so b = a copies the contents of a to variable b.
Implement a Clone method in your Vector3D class (previously, this would mean implementing ICloneable, but this is no longer recommended). Alternatively, you could create a Vector3D constructor that takes another vector as a parameter and creates a copy.
Manually copy the three values yourself (b = new Vector3D(a.x, a.y, a.z)), if you cannot change the implementation of Vector3D.
You may want to change your Vector3D class to a struct. That would let you work with a value type instead of a reference type.
Your other option is to implement ICloneable or use some other method to create a deep copy of your object.
Yes, reference types are assinged by reference.
If you want to have a separate instance, you want to CLONE your instance.
Create a Vector3D.Clone() method, which would look something like this:
public Vector3D Clone()
{
return new Vector3D(this.x, this.y, this.x);
}
Then your Main should look like this:
static void Main(string[] args)
{
Vector3D a, b;
a = new Vector3D(0, 5, 10);
b = new Vector3D(0, 0, 0);
b = a.Clone();
a.x = 10;
Console.WriteLine("vector a=" + a.ToString());
Console.WriteLine("vector b=" + b.ToString());
Console.ReadKey();
}
But as others have said, something as small as a Vector3D would be better suited as an immutable struct
You can make it a struct like Henk says. And you can add a constructor
struct Vector3D
{
public int x;
public int y;
public int z;
public Vector3D(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
}
public override string ToString()
{
return string.Format("{0}, {1}, {2}", x, y, z);
}
}
You could also do this without adding the constructor.
b = new Vector3D() {x=0, y=0, z=0};
No need to use a struct, I suggest you should design your Vector3D as an immutable class. Here are some good examples. Of course, a.x = 10 won't be possible for an immutable class.
Related
I'm creating a grid based game using Vector2Ints to represent positions on the grid. The world needs to be wrapped on the x and y axes, meaning that if the world is 100x100 cells big then for an entity with position = new Vector2Int(99, 99) and displacement = new Vector2Int(1,1), we'd have newPosition = position + displacement equal to Vector2Int(0, 0). The obvious way initially seemed to somehow override the set method behaviour of the Vector2Int struct in Unity because then I can continue to benefit from all the other methods on the struct like addition, multiplication with ints etc. while still getting the "wrapping" functionality with every operation. This is key so I don't have to remember to keep calling a helper function.
The way I thought about achieving this would be to somehow extend the Vector2Int so I can set a mapWidth and mapHeight, and modify the set methods to x = xIn % mapWidth and y = yIn % mapHeight.
I would appreciate suggestions on how best to achieve the above without just duplicating the code, albeit with the minor modifications, of the whole Vector2Int struct.
You can add an extension method to any class like so:
// the class name here doesn't matter. Just some static class.
public static class Helpers
{
public static Vector2Int Wrapped(this Vector2Int v, int wrapX, int wrapY)
{
return new Vector2Int(v.x % wrapX, v.y % wrapY);
}
}
Usage:
Vector2Int vec = new Vector2Int(15,15);
Vector2Int vecWrapped = vec.Wrapped(10, 10);
vecWrapped will now be 5, 5.
It's the "this Vector2Int" in the method parameters that makes it extend the class.
EDIT:
On the question of whether it's possible to override the Set method, not directly no afaik. You can add an extension method called Set, but it can't have the same signature, ie. it can't be Set(x, y). You could add a method called something else though.
And afaik, there's no way to make it automatically 'gridify' after any operation on the vector.
As suggested by aybe, using "this ref" can make this much more usable though, removing the need to assign the return value.
public static class Helpers
{
public static int gridX = 10;
public static int gridY = 10;
public static void SetGrid(this ref Vector2Int v, int x, int y)
{
v.Set(x % gridX, y % gridY);
}
}
Usage:
Vector2Int v = Vector2Int.zero;
v.SetGrid(15, 15);
v will now be 5, 5.
I am experimenting a bit with C# record types and was wondering why the compiler behaves as it does, with the following example.
My code looks like this
public class C {
public void M() {
Point p1 = new Point(3, 3);
Point p2 = p1 with { Y = 4 };
}
}
record Point(int X,int Y);
Looking at Sharplab.io, its seems like the compiler adds on an extra reference for p2.
public void M()
{
Point point = new Point(3, 3);
Point point2 = point.<Clone>$();
point2.Y = 4;
Point point3 = point2;
}
(It only does this when I mutate the record (non destructively, of course)).
Why is that?
This code doesn't create an additional object instance or a new reference. A variable isn't a storage location, it's essentially a name by which we refer to an object instance.
with creates a clone of the original object and modifies some of its values. That's what this code shows. point3 isn't a new reference. This part :
p1 with { Y = 4 }
is translated to
Point point2 = point.<Clone>$();
point2.Y = 4;
The result of that operation is finally stored in the target variable with :
Point point3 = point2;
Record types are reference types, so the final assignment doesn't result in copies or allocations. .NET isn't reference-counted, so the variable assignment doesn't cost either.
Exception safety
Even if there was a different way to write such code (there isn't), it's safer to construct the full object separately and then assign it to its final location. This means that should something go wrong we can just discard the new instance than try to revert any changes made to the half-completed object.
I'm using C# XNA and I found when trying to add two points together it won't let me. Is there some way I can add to the Point class to allow this code to run.
Point a = new Point(3,4);
Point b = new Point(6,2);
Point c = a + b; //Should equal new Point(9,6);
You could simply overload the + operator - like this:
class Point
{
public int X { get; private set; }
public int Y { get; private set; }
public Point(int x, int y)
{
X = x;
Y = y;
}
public static Point operator +(Point p1, Point p2)
{
return new Point(p1.X + p2.X, p1.Y + p2.Y);
}
}
Now, your code compiles as work as you expect it to:
Point a = new Point(3, 4);
Point b = new Point(6, 2);
Point c = a + b; //Should equal new Point(9,6); - and it is :)
More info on operator overloading can be found on MSDN.
Add to a separate class.
public static void Add(this Point a, Point b){
a.X += b.X;
a.Y += b.Y;
}
You could use extension methods as shown below
class Program
{
static void Main(string[] args)
{
Point a = new Point(1, 2);
Point b = new Point(2, 4);
Point c=a.AddPoints(b);
}
}
public static class MyExtensions
{
public static Point AddPoints(this Point x, Point y)
{
return new Point(x.X + y.X, x.Y + y.Y);
}
}
Technically, you could use a static method, extension method and maybe even an operator overload, as other answers suggest.
However, .NET has both Point and Size to capture a semantic distinction:
Point refers to a point, usually compared to an arbitrary reference point.
Size refers to a vector - distance with a direction, or distance X & distance Y.
Adding Points would be meaningless. For example, if Points refer to coordinates on a geographic map - what would their mean? "50° latitude" + "60° latitude" = "110° latitude"? That's why .NET wisely chose not to implement an addition operator.
However, adding Point and Size can have reasonable meaning - "50° latitude" + "1° latitude distance" = "51° latitude" is a good answer.
PS. Notice the similarity to the distinction between DateTime and TimeSpan.
TL;DR - One or both of your Points should actually be a Size - change it at the earliest location possible.
You can add a Size struct to a Point struct , and easily convert between them by passing a Point in to a Size constructor like this:
c = a + (new Size(b));
Why DOT NET choose to enforce this subtle distinction between intensive and extensive tuples is beyond me.
Consider the TranslateAllCoords static function:
static class CoordinateTransformation
{
public static void TranslateAllCoords(ref int x, ref int y, ref int z,
int amount)
{
x+=amount;
y+=amount;
z+=amount;
}
}
Then, later in code, you have:
int x=0, y=0, z=0;
...
CoordinateTransformation.TranslateAllCoords(ref x, ref y, ref z, 5);
...
But, by calling TranslateAllCoords you are in effect modifying value types (i.e., the integer coords) and generally values types should be immutable. Are some rules broken here or is this a perfectly valid construct that gets around the "value types should be immutable" construct by modifying only built in value types?
The values are immutable. The variables that contain the value types are mutable. Variables vary, that's why they're called "variables".
The design guidance that value types should be immutable is essentially saying that you should not try to change only part of a variable. When you say
struct Point { public int X; public int Y; public int Z; }
...
Point p = new Point();
p.X = 123;
then what you are saying is "mutate only part of the variable p". That is confusing. The variable p should logically represent a point, and a point is a value. If you want to vary p, then logically vary the whole thing by assigning a new point to it. Don't mutate one point into another.
But even if we made point immutable:
struct Point { public int X { get; private set; } ... etc }
then a variable of that type can still vary!
Point p = new Point(123, 456, 789);
p = new Point(100, 200, 300);
But now it is clear that the entire variable is changing to a new point, rather than us trying to mutate a particular portion of the variable.
With an immutable value type you can then do your translation more sensibly:
static Point Translate(Point p, int offset)
{
return new Point(p.X + offset, p.Y + offset, p.Z + offset);
}
...
Point p = new Point(100, 200, 300);
p = Translate(p, 5);
See, again, p mutates, but it mutates all at once, not in little bits at a time.
No rules are broken there. You're simply creating a new integer value and reassigning the variable containing them.
In the context of C#, .Net 4...
Given a data source object that supplies vertices by index from an array of doubles where a vertex contains ten doubles with members Px, Py, Pz, Nx, Ny, Nz, S, T, U, V. and the backing array contains all or any subset of vertex members based on the data source's stride, offset and count properties. The data source could be simplified as:
public class DataSource
{
private double[] data;
private int offset, stride, count;
public double[] ElementAt(int index)
{
double[] result = new double[count];
var relativeIndex = index * stride + offset;
Array.Copy(data, relativeIndex, result, 0, count);
return result;
}
.
.
.
}
Some consumers would be interested in a return type of double[], but most would request data as a PointNf type where N is the number of vertex members taken (Point1f...Point10f). The Point type consumer does not care for the source's stride and the source supplies a zero for members greater than its stride. e.g. Point4f from a source of stride 3 would be filled with data[i + 0], data[i + 1], data[i + 2], 0.
Obviously DataSource could expose methods GetPoint1f(int index), GetPoint2f(int index) and the like. These types of solutions might be best given the fixed set of return types, element size, etc. However...
What are possible solutions if a syntax like…
Point3f point = SomeDataSource.ElementAt[index];
...or similar was requested/required/desired ?...pros/cons ?...examples of what not to do ?...harsh language ?
What are possible solutions if a syntax like…
Point3f point = SomeDataSource.ElementAt[index];
...or similar was requested/required/desired ?
This is the prime candidate for a user-defined implicit conversion:
// I’m guessing it’s a struct, but can be a class too
public struct Point3f
{
// ...
public static implicit operator Point3f(double[] array)
{
// Just for demonstration: in real code,
// please check length and nullity of array first!
return new Point3f(array[0], array[1], array[2]);
}
// You can declare one that goes the other way too!
public static implicit operator double[](Point3f point)
{
return new double[] { point.Px, point.Py, point.Pz };
}
}
How do you know the type of PointNf which is at index?
Anyway, the factory pattern is what you're after, but the question is how you will implement it given your objects. A basic factory would look something like this
public T GetElementAt<T>(int index) where T : new() {
Type type = typeof(T);
T result = new T()
if (type==typeof(Point3f)) {
result.X = data[index];
result.Y = data[index+1];
result.Z = data[index+2];
}
else if (type==typeof(Point2f) {
result.X = data[index];
result.Y = data[index+1];
}
return result;
}
NB: This won't compile because X, Y, Z aren't defined for T, but you don't wanna use this anyway.
The drawback is having to check for the type of T for each point type. There are several solutions to improve on it though if you are using your own PointNf classes which you can modify. An example would be to make each PointNf class derive from a common interface (IPointDataProvider) with a method like void PopulateData(double[] data, int index), for which they implement their own specifics, and your factory method can be reduced to
public T GetElementAt<T>(int index) where T : IPointDataProvider, new() {
T result = new T()
result.PopulateData(data, index)
return result;
}
An example implementation of a point is as simple as.
class Point2f : IPointDataProvider {
double X, Y;
void IPointDataProvider.PopulateData(double[] data, int index) {
X = data[0];
Y = data[1];
}
}