# Class or struct?

I guess this question has been answered before, but would like to know some more info on it. I have done a custom struct that is just a Vector2 with int values instead of float. Is nothing fancy is just x,y and few operators and functions, not all the functions a real Vector2 have as I don’t need them.

What I’m not sure is about performance, here is my struct:

``````public struct Vector2Int {
public int x,y;

//Simula Vector2.zero
static public Vector2Int zero
{
get
{
return new Vector2Int(0,0);
}
}

//Simula Vector2.one
static public Vector2Int one
{
get
{
return new Vector2Int(1,1);
}
}

//Simula Vector2.up
static public Vector2Int up
{
get
{
return new Vector2Int(0,1);
}
}

//Simula Vector2.right
static public Vector2Int right
{
get
{
return new Vector2Int(1,0);
}
}

//Vector2Int con overload
public Vector2Int (int a, int b)
{
x = a;
y = b;
}

//Operatore addizione
static public Vector2Int operator +(Vector2Int a, Vector2Int b)
{
return new Vector2Int(a.x + b.x, a.y + b.y);
}

//Operatore sottrazione
static public Vector2Int operator -(Vector2Int a, Vector2Int b)
{
return new Vector2Int(a.x - b.x, a.y - b.y);
}

//Operatore moltiplicazione
static public Vector2Int operator *(Vector2Int a, Vector2Int b)
{
return new Vector2Int(a.x * b.x, a.y * b.y);
}

//Overrride funzione ToString
public override string ToString()
{
return "(" + x + ", " + y + ")";
}
}
``````

I have the same also for Vector3 and Vector4, but no need to past them too.
I don’t need to have it serializable in editor as is always set at runtime, but I’m not sure if would be better as class rather than struct.

For performance I believe you are best not to use a struct for anything over 16 bytes, see this for more details http://msdn.microsoft.com/en-us/library/ms229017(v=vs.110).aspx There are other factors to consider as well, which are also mentioned in this article.

From what I recall, Unity has big issue serializing struct that are not native to itself.
If serialization is not an issue for you, that could work.

Also, like Munchy’s link explains, it also depends of your use. Unity’s Vector2-3-4 are useful because of the way they are handled. In Unity, when you pass a Vector around, you don’t want to manually make copies of it all the time. Having it as a struct makes sense.

When passing large struct types around it is better to pass by reference instead of by value. I made this change to my matrix manipulation logic and it made a massive difference when I profiled the code:

``````public void CalculateSomeMatrix(out Matrix4x4 mat) {
Matrix4x4 someOtherMatrix = transform.localToWorldMatrix;
DoSomethingToMatrix(out mat, ref someOtherMatrix);
}

private void DoSomethingToMatrix(out Matrix4x4 mat, ref Matrix4x4 otherMat) { ... }

// Output matrix is written straight to result memory without
// needing to be copied several times as method returns.
Matrix4x4 result;
CalculateSomeMatrix(out result);
``````

I use it only for movements in a grid based system, a vector2 of ints help me to get straight into a 2D array with just a simple function. I use the values also to add to the real transform of the object for movement in worldspace. This is the only use for now.

Here a small portion of the movement code:

``````using UnityEngine;

public class PlayerBehaviour : MonoBehaviour {

private Transform t;				//Questo transform (caching)
private EXMainCamera cam;			//La camera principale
private float nStep;				//Ritardo per il prossimo passo

public DIRECTION direction;			//Direzione corrente
public Vector2Int position;			//Posizione nella griglia

public EXTerrain terrain;			//Il tipo di terreno corrente

//Prepara il giocaotre
void Start () {
t = this.transform;

if (!cam)
cam = Camera.main.GetComponent<EXMainCamera> ();

position = new Vector2Int (3, 1);

MoveInGrid (direction, position);
}

//Movimento nella griglia
public void MoveInGrid(DIRECTION dir, Vector2Int cell) {

if(EXSystem.Navigation.CanMoveInGrid(cell)) {
position = cell;
terrain = EXSystem.Navigation.ReturnTerrainInGrid (position);
t.position = new Vector3(position.x + .5f, -position.y - .5f, t.position.z);
}

direction = dir;

cam.CameraFollow ();

nStep = Time.time + terrain.friction;
}

// Input di movimento
void Update () {

if (Input.GetKeyDown ("Up") Time.time > nStep)
MoveInGrid(DIRECTION.North, new Vector2Int(position.x,position.y - 1));

}
}
``````

Your struct is fine. It’s 2 integers. It’s only 8 bytes in size for those two fields. I wouldn’t worry much about it. You don’t need to pass as ref or anything like that… it’s smaller in size than Vector3 is, and the same size as Vector2.

I will say though that I don’t like the idea of calling it a Vector, because it doesn’t act like a Vector, which comes from math and has very specific properties to it that yours does not (and can not) have.

Yeah but is more about recognition for myself as I’m the only one that is gonna work with the code, so I’m ok with it. But I get your point.

One thing that I would always do with custom structs is to implement the IEquatable namespace since this helps to avoid boxing/unboxing which occur when using certain .NET collection types (the generic ones). Also, instead of using static properties for constant values, just use readonly values since this is a little faster.

Here is how I would probably implement this struct (warning, not tested):

``````public struct Vector2Int : IEquatable<Vector2Int> {

public static readonly Vector2Int zero = new Vector2Int(0, 0);
public static readonly Vector2Int one = new Vector2Int(1, 1);
public static readonly Vector2Int up = new Vector2Int(0, 1);
public static readonly Vector2Int right = new Vector2Int(1, 0);

public int x;
public int y;

public Vector2Int(int x, int y) {
this.x = x;
this.y = y;
}

public Vector2Int traverse {
get {
Vector2Int value;
value.x = y;
value.y = x;
return value;
}
}
public Vector2 inverse {
get {
Vector2Int value;
value.x = -x;
value.y = -y;
return value;
}
}

public override string ToString() {
return string.Format("X: {0}, Y: {0}", x, y);
}

public bool Equals(Vector2Int other) {
return x == other.x  y == other.y;
}

public override bool Equals(object obj) {
if (!(obj is Vector2Int))
return false;
Vector2Int other = (Vector2Int)obj;
return x == other.x  y == other.y;
}

public override int GetHashCode() {
return x ^ y;
}

public static bool operator == (Vector2Int lhs, Vector2Int rhs) {
return lhs.x == rhs.x  lhs.y == rhs.y;
}
public static bool operator != (Vector2Int lhs, Vector2Int rhs) {
return lhs.x != rhs.x  lhs.y != rhs.y;
}

public static Vector2Int operator + (Vector2Int lhs, Vector2Int rhs) {
lhs.x += rhs.x;
lhs.y += rhs.y;
return  lhs;
}
public static Vector2Int operator - (Vector2Int lhs, Vector2Int rhs) {
lhs.x -= rhs.x;
lhs.y -= rhs.y;
return  lhs;
}
public static Vector2Int operator - (Vector2Int value) {
value.x = -value.x;
value.y = -value.y;
return  value;
}

public static Vector2Int operator * (Vector2Int lhs, Vector2Int rhs) {
lhs.x *= rhs.x;
lhs.y *= rhs.y;
return  lhs;
}
public static Vector2Int operator / (Vector2Int lhs, Vector2Int rhs) {
lhs.x /= rhs.x;
lhs.y /= rhs.y;
return  lhs;
}
public static Vector2Int operator % (Vector2Int lhs, Vector2Int rhs) {
lhs.x %= rhs.x;
lhs.y %= rhs.y;
return  lhs;
}

public static implicit operator Vector2Int(Vector2 vec) {
Vector2Int result;
result.x = (int)vec.x;
result.y = (int)vec.y;
return result;
}
public static implicit operator Vector2Int(Vector3 vec) {
Vector2Int result;
result.x = (int)vec.x;
result.y = (int)vec.y;
return result;
}
public static implicit operator Vector2(Vector2Int vec) {
Vector2 result;
result.x = vec.x;
result.y = vec.y;
return result;
}
public static implicit operator Vector3(Vector2Int vec) {
Vector3 result;
result.x = vec.x;
result.y = vec.y;
return result;
}

}
``````

I have just updated that source code with implicit conversion operators for the Unity Vector2 and Vector3 types.

I would agree, “Point” or “Point2” would be a more accurate name.

Thanks for the tips, didn’t know with readonly would be faster. So for what I understand from reading also the msdn docs, adding System.IEquatable interface would prevent the use of Reflection and would be faster when using for example a List

It simply avoids an allocation each time yourStruct.Equals(anotherStruct) is used. This is applicable in cases when using generic collections like List, HashSet, Dictionary, etc.

For instance:

``````List<Vector2Int> listOfPoints = FetchListOfPoint();

if (listOfPoints.Contains(Vector2Int.zero)) {
// If IEquatable<Vector2Int> is implemented, we just avoided potentially a LOT of allocations!
}
``````

The boxing allocation occurs since you are otherwise passing a value type into a function which expects a reference. If you cast to (object) then you incur the allocation since the value has to be ‘boxed’ into a reference type.

Not exactly… The biggest reason for IEquatable is so you can do comparisons without boxing and unboxing occurring, which sometimes happen when you don’t realize it, as well as for use with Generics. It allows you to explicitly define your equality comparison.

Another point of consideration is garbage collection. I don’t typically get overly hung up on the “16 byte” rule. To illustrate why that rule is in place, let’s say you have the following function:

``````DoSomething(Foo bar);
``````

Now, if “Foo” is a class, then only the 32-bit (4 byte) pointer to that instance is passed. Whereas if it’s a struct, a copy of the object gets passed. Let’s say your struct itself is actually 16 bytes. You could pass 4 integers (or 4 class references) to a function for the same approximate cost of passing 1 struct. And since the struct gets copied, you’re allocating more memory, however it’s on the stack (not the heap) so it’s more efficient and not subject to garbage collection.

So, if you’re not passing your structs around a lot, even if they’re larger than 16 bytes it may not hurt you… especially if you’re creating a lot of them as you won’t suffer from allocation on the heap and garbage collection.

There is another catch though… and that is when using collections. Any time an array has to be resized or added to another array… it has to copy all of that data (and with larger structs this becomes more expensive). This is one place where generics can bite you as well. Consider the following:

``````//Define generic list of Foo with initial capacity of 4 elements
List<Foo> fooList = new List<Foo>(4);

//Add 100 "Foos" to the list
for(var i = 0; i < 100; i++)
{
fooList.Add(new Foo());
}
``````

By default a List starts with a capacity of “0” but her we specified it as 4 just for fun. If we didn’t specify it, the first time you add an item it will set the capcity to “4”, then it will double every time you exceed it (so when add the 5th element it becomes 8, when you add the 8th element it becomes 16, etc.).

Internally the List stores items in an array so every time that array has to be resized it results in a copy of the data… so in the case of structs actually anything larger than 4 bytes (32-bit) will be more costly to copy/resize than a reference type because it’s copying more data.

One more point to note… recursion. If you’re writing recursive functions it can come back to haunt you. First of all, every function call itself is stored on the stack… As the function recurses with classes it passes a reference (32-bit pointer)… but with structs it passes a copy so if you have large structs you can start eating up memory.

Just some things to chew on and think about. Use what makes sense to you and your application. The best thing to do is just try… do some real world tests and see how it impacts your application. It’s pretty simple to just change from Class to Struct (you don’t even have to remove your IEquatable implementation) and see what the performance is like.

I guess I can’t tell yet how far I’m going with this performance wise as I’m doing a simple operation that is hard to quantify for now, but I appreciate all the technical info, is really something to keep in mind if stuff start falling apart later on. And yeah changing from struct to class isn’t a big deal, so I’m safe to go on and change later without a load of work.

Thanks everyone for the insight into the matter.