Reputation: 137
I am using a custom struct as a key in a dictionary:
public struct TV
{
public int B;
public int BC;
public int P;
public int PO;
public int PC;
public int W;
public int WO;
public int WC;
public int R;
public int RO;
public int RC;
public int G;
public int GO;
public int GC;
public int GW;
public int GWO;
public int GWC;
public TV(int b, int bC, int p, int po, int pC, int w, int wo, int wC, int r, int ro, int rC, int g, int go, int gC, int gw, int gwo, int gwC)
{
B = b;
BC = bC;
P = p;
PO = po;
PC = pC;
W = w;
WO = wo;
WC = wC;
R = r;
RO = ro;
RC = rC;
G = g;
GO = go;
GC = gC;
GW = gw;
GWO = gwo;
GWC = gwC;
}
}
However as I am using .containskey and getkey I am getting insane amounts of garbage collection allocations numbering in the millions per frame
I have researched this problem and I understand that it has to do with improper boxing of the struct due to not implementing IEquatable and overrides for some methods like equals() and getHashCode.
I have seen some examples of how to implement those but I only found examples for small structs of 2 or 3 variables, since my struct has 17 values I am at a loss on how I should implement this also because I don't understand how hashcodes work, I would greatly appreciate it if someone could guide me in the right direction, what should I add to this struct to make it usable as a dictionary key?
Upvotes: 5
Views: 3332
Reputation: 1062650
The key here is to:
IEquatable<TV>
(it will be invoked via "constrained" call, not via boxing)GetHashCode()
with a suitable hash function over whatever fields you want to compareEquals(TV)
with an equality check that aligns with GetHashCode()
Equals(object)
as => obj is TV typed && Equals(typed);
This should avoid all the boxing and reflection.
Here's one way of doing it:
public struct TV : IEquatable<TV>
{
public override string ToString() => nameof(TV);
public int B;
public int BC;
public int P;
public int PO;
public int PC;
public int W;
public int WO;
public int WC;
public int R;
public int RO;
public int RC;
public int G;
public int GO;
public int GC;
public int GW;
public int GWO;
public int GWC;
public TV(int b, int bC, int p, int po, int pC, int w, int wo, int wC, int r, int ro, int rC, int g, int go, int gC, int gw, int gwo, int gwC)
{
B = b;
BC = bC;
P = p;
PO = po;
PC = pC;
W = w;
WO = wo;
WC = wC;
R = r;
RO = ro;
RC = rC;
G = g;
GO = go;
GC = gC;
GW = gw;
GWO = gwo;
GWC = gwC;
}
public override bool Equals(object obj) => obj is TV other && Equals(other);
public bool Equals(TV other)
{
return B == other.B &&
BC == other.BC &&
P == other.P &&
PO == other.PO &&
PC == other.PC &&
W == other.W &&
WO == other.WO &&
WC == other.WC &&
R == other.R &&
RO == other.RO &&
RC == other.RC &&
G == other.G &&
GO == other.GO &&
GC == other.GC &&
GW == other.GW &&
GWO == other.GWO &&
GWC == other.GWC;
}
public override int GetHashCode()
{
var hash = new HashCode();
hash.Add(B);
hash.Add(BC);
hash.Add(P);
hash.Add(PO);
hash.Add(PC);
hash.Add(W);
hash.Add(WO);
hash.Add(WC);
hash.Add(R);
hash.Add(RO);
hash.Add(RC);
hash.Add(G);
hash.Add(GO);
hash.Add(GC);
hash.Add(GW);
hash.Add(GWO);
hash.Add(GWC);
return hash.ToHashCode();
}
}
Upvotes: 11