KeyNotFoundException in filled dictionary - c#

I am trying to modify value in dictionary, but the compiler throws KeyNotFoundException. I'm sure, I declared that key in dictionary, because I am calling GenerateEmptyChunks() method, which fills dictionary with chunks with key of their position and values are empty for level generator. I've checked debugger and Chunks dictionary object is correctly filled with keys and values. Is it caused by my unworking CompareTo method? If yes, how I have modify CompareTo method to return right values?
public Dictionary<WPoint, WChunk> Chunks = new Dictionary<WPoint, WChunk>();
GenerateEmptyChunks() method:
public void GenerateEmptyChunks(int Xcount, int Ycount)
{
for(int x = 0; x <= Xcount; x++)
{
for (int y = 0; y <= Ycount; y++)
{
this.Chunks.Add(new WPoint(x, y), new WChunk(x, y));
}
}
}
AddBlock() method which is called by level generator for each tile:
public void AddBlock(WPoint location, int data)
{
this.Chunks[location.GetChunk()].AddTile(new WTile(location, data));
}
WChunk object:
public class WChunk
{
public int ChunkX;
public int ChunkY;
public SortedDictionary<WPoint, WTile> Tiles = new SortedDictionary<WPoint, WTile>();
public WChunk(int posX, int posY)
{
ChunkX = posX;
ChunkY = posY;
}
public void AddTile(WTile tile)
{
Tiles.Add(tile.GetLocation(), tile);
}
}
WPoint object:
public class WPoint : IComparable
{
public float X;
public float Y;
public WPoint(float x, float y)
{
X = x;
Y = y;
}
public WPoint GetChunk()
{
//Oprava pre bloky mensie ako (1,1)
if (X <= 16 && Y <= 16)
{
return new WPoint(0, 0);
}
else
{
double pX = (double)(X / 16);
double pY = (double)(Y / 16);
return new WPoint((int)Math.Floor(pX), (int)Math.Floor(pY));
}
}
public int CompareTo(object obj)
{
WPoint point2 = (WPoint)obj;
if (point2.X == this.X && point2.Y == this.Y)
{
return 0;
}
else if (point2.X >= this.X && point2.Y >= this.Y)
{
return -1;
}
else
{
return 1;
}
}
}
Any ideas why is compiler rejecting keys, when they are in dictionary?

Yes. You have not overridden GetHashCode.

Dictionary is using the GetHashCode and Equals for key comparisons, so implementing the IComparable interface is not enough. Have a look at this answer, that's exactly what you need.

Related

System.Drawing.Point is much faster than my simple struct

What wizardry is in that System.Drawing.Point class that makes it so much faster than my simple struct?
It's quite a bit faster. I'm getting 1-5ms on Point class and 2000ms or more on my struct.
Looking at the Points.cs source, I'm not skilled enough to spot what is doing it. I made an attempt at implementing IEquatable (probably incorrectly) and couldn't make any gains.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
class Program
{
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
int elementsInSets = 10000;
int lookupCount = 10000;
// Point struct from System.Drawing
HashSet<Point> myPoints = new HashSet<Point>();
for (int i = 0; i < elementsInSets; i++)
{
myPoints.Add(new Point(i, i));
}
// My simple struct
HashSet<P> myPoints2 = new HashSet<P>();
for (int i = 0; i < elementsInSets; i++)
{
myPoints2.Add(new P(i, i));
}
sw.Start();
for (int j = 0; j < lookupCount; j++)
{
if (myPoints2.Contains(new P(j, j)))
{
//found
}
}
Console.WriteLine("simple P " + sw.ElapsedMilliseconds + "ms");
sw.Restart();
for (int j = 0; j < lookupCount; j++)
{
if (myPoints.Contains(new Point(j, j)))
{
// found
}
}
Console.WriteLine("Point " + sw.ElapsedMilliseconds + "ms");
}
}
public struct P
{
int x;
int y;
public P(int xCoord, int yCoord)
{
x = xCoord;
y = yCoord;
}
}
That's due to no override for GetHashCode (you should also override Equals) as in the Point source. They do it this way:
public override bool Equals(object obj) {
if (!(obj is Point)) return false;
Point comp = (Point)obj;
// Note value types can't have derived classes, so we don't need
// to check the types of the objects here. -- Microsoft, 2/21/2001
return comp.X == this.X && comp.Y == this.Y;
}
public override int GetHashCode() {
return unchecked(x ^ y);
}
If your implementation was the same you should see similar performance.
While a struct provides a default implementation for Equals and GetHashCode they have bad performance as they use reflection. Instead you should provide your own implementation. While you don't have to implement IEquatable<Point> I think it's worthwhile:
readonly struct Point : IEquatable<Point>
{
public Point(int x, int y)
{
X = x;
Y = y;
}
public int X { get; }
public int Y { get; }
public bool Equals(Point other) => X == other.X && Y == other.Y;
public override bool Equals(object obj) => obj is Point point && Equals(point);
public override int GetHashCode() => HashCode.Combine(X, Y);
}
I did a casual benchmark using your code and the performance of this code is similar to System.Drawing.Point or perhaps slightly slower but not thousands of times slower like the naïve approach.

Sorting an ArrayList by using IComparer Compare Function C#

How do I sort a List of Points by using an customized compare method?
using System;
using System.Collections;
using System.Collections.Generic;
public class Point : IComparer<Point>
{
public int x;
public int y;
public Point(int x_Point, int y_Point)
{
x = x_Point;
y = y_Point;
}
public int Compare(Point a, Point b)
{
if (a.x == b.x && a.y == b.y)
return 0;
if (a.y < b.y)
return -1;
if (a.y == b.y && a.x < b.x)
return -1;
return 1;
}
}
The code below throws an error at AL.sort().
"Failed to compare two elements in the array."
"ArgumentException: At least one object must implement IComparable"
I have no clue why. Did I described my own comparing method at the Points class wrong?
public class ArrayListTest
{
public static void Main(string[] args)
{
ArrayList AL = new ArrayList();
Random R = new Random();
for (int i = 0; i < 10; i++)
{
Point p = new Point(R.Next(50), R.Next(50));
AL.Add(p);
}
PrintValues(AL);
AL.Sort();
PrintValues(AL);
}
}
You'd better use the IComparable<> interface.
"The object to be sorted will implement IComparable while the class that is going to sort the objects will implement IComparer."
Source: difference between IComparable and IComparer
public class Point : IComparable<Point>
{
public int x;
public int y;
public Point(int x_Point, int y_Point)
{
x = x_Point;
y = y_Point;
}
public int CompareTo(Point other)
{
if (this.x == other.x && this.y == other.y)
return 0;
if (this.y < other.y)
return -1;
if (this.y == other.y && this.x < other.x)
return -1;
return 1;
}
}
public static void Main()
{
var AL = new List<Point>(); // ditch the ArrayList for good... ;-)
Random R = new Random();
for (int i = 0; i < 10; i++)
{
Point p = new Point(R.Next(50), R.Next(50));
AL.Add(p);
}
PrintValues(AL);
AL.Sort();
PrintValues(AL);
}

Dictionary doesn't find value when using new object as key

In Unity 3D, I am trying to build a Dictionary of PathNodes keyed by NodeCoordinates. I have overridden GetHashCode in NodeCoordinate and it should return a constant unique value. If I loop through the keys of the dictionary, the lookup works fine, but if I create a new NodeCoordinate with the coordinates of a PathNode that should exist, the lookup fails even though the hash codes are equal.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PathNode : MonoBehaviour {
public static Dictionary<NodeCoordinate, PathNode> pathNodes;
public PathNode left;
public PathNode right;
public PathNode forward;
public PathNode backward;
private NodeCoordinate coord;
void Awake()
{
if (PathNode.pathNodes == null)
{
PathNode.pathNodes = new Dictionary<NodeCoordinate, PathNode>();
}
NodeCoordinate coord = new NodeCoordinate(transform.position.x, transform.position.z);
this.coord = coord;
PathNode.pathNodes.Add(coord, this);
NodeCoordinate leftChord = new NodeCoordinate(coord.x - 1, coord.z);
NodeCoordinate rightChord = new NodeCoordinate(coord.x + 1, coord.z);
NodeCoordinate forwardChord = new NodeCoordinate(coord.x, coord.z + 1);
NodeCoordinate backwardChord = new NodeCoordinate(coord.x, coord.z - 1);
if (PathNode.pathNodes.ContainsKey(leftChord))
{
this.left = PathNode.pathNodes[leftChord];
this.left.right = this;
}
if (PathNode.pathNodes.ContainsKey(rightChord))
{
this.right = PathNode.pathNodes[rightChord];
this.right.left = this;
}
if (PathNode.pathNodes.ContainsKey(forwardChord))
{
this.forward = PathNode.pathNodes[forwardChord];
this.forward.backward = this;
}
if (PathNode.pathNodes.ContainsKey(backwardChord))
{
this.backward = PathNode.pathNodes[backwardChord];
this.backward.forward = this;
}
}
private static bool debug = true;
void Update()
{
if (debug)
{
foreach (NodeCoordinate coord in PathNode.pathNodes.Keys)
{
Debug.Log(coord + " : " + PathNode.pathNodes[coord] + " : " + coord.GetHashCode());
}
foreach (PathNode node in PathNode.pathNodes.Values)
{
NodeCoordinate leftChord = new NodeCoordinate(node.coord.x - 1, node.coord.z);
PathNode leftNode;
Debug.Log("Left: " + leftChord + " : " + PathNode.pathNodes.TryGetValue(leftChord, out leftNode) + " : " + leftChord.GetHashCode());
}
debug = false;
}
}
}
public class NodeCoordinate
{
public float x;
public float z;
public NodeCoordinate(float x, float z)
{
this.x = x;
this.z = z;
}
public bool Equals(NodeCoordinate coord)
{
return (this.x == coord.x && this.z == coord.z);
}
public override int GetHashCode()
{
return string.Format("{0}x{1}", this.x, this.z).GetHashCode();
}
public override string ToString()
{
return "Coordinate: " + this.x + " x " + this.z;
}
}
This is the output from my little debug:
debug log
As you can see, when looping through the keys, the lookups with hashcodes 2137067561 and 1824497336 work, but when I instantiate a new NodeCoordinate and try to look it up, it has the same hashcode but the lookup fails. Any idea why this is happening? Thanks.
The problem is that your NodeCoordinate class doesn't define equality in a way that the dictionary would use. You have a method like this:
public bool Equals(NodeCoordinate coord)
... but you neither override IEquatable<NodeCoordinate> nor do you override Equals(object).
Personally I'd suggest doing both - and implementing a simpler hash code that doesn't require string formatting:
public sealed class NodeCoordinate : IEquatable<NodeCoordinate>
{
public float X { get; }
public float Z { get; }
public NodeCoordinate(float x, float z)
{
X = x;
Z = z;
}
public override Equals(object other) => Equals(other as NodeCoordinate);
public bool Equals(NodeCoordinate coord) =>
coord != null && this.X == coord.X && this.Z == coord.Z;
public override int GetHashCode()
{
int hash = 23;
hash = hash * 31 + X;
hash = hash * 31 + Z;
return hash;
}
public override string ToString() => $"Coodinate: {X} x {Z}";
}
(Note that I've also made it immutable and used properties instead of public fields. I've used C# 6 syntax for simplicity, but it wouldn't be hard to translate to C# 5 if necessary.)
So for some reason, when I add a IEqualityComparer with the exact same logic, it works.
Changed the declaration of the dictionary to this:
if (PathNode.pathNodes == null)
{
PathNode.pathNodes = new Dictionary<NodeCoordinate, PathNode>(new NodeCoordinateComparer());
}
Added this:
public class NodeCoordinateComparer : IEqualityComparer<NodeCoordinate>
{
public bool Equals(NodeCoordinate a, NodeCoordinate b)
{
return (a.x == b.x && a.z == b.z);
}
public int GetHashCode(NodeCoordinate coord)
{
return string.Format("{0}x{1}", coord.x, coord.z).GetHashCode();
}
}

Invoke: how can I assign UI obj from BackgroundWorker? C#

Here is the core of my code. I've tried Invoke every which way to China, but I always get the same big red X and an ObjectDisposed exception on Form1. (Edited out unrelated code for brevity.)
using System;
using System.Globalization;
using System.ComponentModel;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using KnownColorsPalette;
namespace Color_Visualizer
{
public partial class Form1 : Form
{
static CultureInfo m_culture = CultureInfo.CurrentCulture;
FastPixel m_fp; // FastPixel encapsulates
// Bitmap.LockBits() funcs & data
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
double fAngle, fRadius, d;
Point3D vU, vV;
Point pLocation = new Point();
do
{
m_pNormal = Coord.Plane.Normal;
fAngle = Math.Atan2(m_pNormal.y, m_pNormal.x);
fRadius = Math.Sqrt(m_pNormal.x * m_pNormal.x + m_pNormal.y * m_pNormal.y);
fAngle += fSpeed * 180 / Math.PI;
m_pNormal.x = Math.Cos(fAngle) * fRadius;
m_pNormal.y = Math.Sin(fAngle) * fRadius;
m_fp.Lock();
m_fp.Clear(Color.Black);
foreach (FoundColors fc in m_lColors.Values)
{
vU = new Point3D(Coord.U);
d = dist(fc.pt, ref vU);
vV = Coord.V;
vV.mult(d);
pLocation.X = (int)(m_midHoriz + vU.norm());
pLocation.Y = (int)(m_midVert + vV.norm());
m_fp.SetPixel(pLocation, fc.color);
}
m_fp.Unlock();
Invoke((MethodInvoker)delegate { pictureBox1.Image = m_fp.Bitmap; });
} while (true);
}
public Form1()
{
InitializeComponent();
WindowState = FormWindowState.Maximized;
CoordinateSystem.AssignMe(this);
}
void ReadColors() // I saved all of Wikipedia's 1200+ named colors
{ // as a text file.
}
private void Form1_Load(object sender, EventArgs e)
{
ReadColors();
Point3D p = new Point3D(127.5, 127.5, 127.5);
foreach (FoundColors fc in m_lColors.Values)
fc.pt = fc.color - p; // My Point3D class has
// implicit operator casts to and
// from Color.
Coord = new CoordinateSystem(new Plane(new Point3D(-127.5, -127.5, -127.5), new Point3D(-1, 0, 0)));
backgroundWorker1.RunWorkerAsync();
}
double fSpeed = 5;
Point3D m_pNormal = new Point3D();
double m_fDist, m_fDot;
public double dist(Point3D pt, ref Point3D vU)
{
double c1 = pt.dot(vU);
double c2 = vU.dot(vU);
double b = c1 / c2;
vU.mult(b);
//m_fDot = pt.dot(Coord.Normal);
m_fDist = pt.norm(pt - vU);
return m_fDist;
}
double m_midHoriz, m_midVert;
private void Form1_Resize(object sender, EventArgs e)
{
m_midHoriz = pictureBox1.Width / 2;
m_midVert = pictureBox1.Height / 2;
m_fp = new FastPixel(new Bitmap(pictureBox1.Width, pictureBox1.Height));
}
}
}
This is just support code, not central to my question:
Sorting.cs
using System.Collections.Generic;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
using KnownColorsPalette;
namespace Color_Visualizer
{
public partial class Form1 : Form
{
public static ColorComp m_compClr = new ColorComp();
public static NameComp m_compName = new NameComp();
SortedList<Color, FoundColors> m_lColors = new SortedList<Color, FoundColors>(m_compClr);
SortedList<string, FoundColors> m_lColorByName = new SortedList<string, FoundColors>(m_compName);
[DebuggerDisplayAttribute("{name}, R={color.R}, G={color.G}, B={color.B}")]
class FoundColors
{
public Color color = Color.Empty;
public string name = "";
public CIELab_Color cLab;
public Point3D pt;
public List<int> lClosest = new List<int>();
public int nFarthest;
public FoundColors(FoundColors fc)
{
color = fc.color;
name = fc.name;
cLab = new CIELab_Color(fc.cLab.CIE_L, fc.cLab.CIE_a, fc.cLab.CIE_b);
lClosest.AddRange(fc.lClosest);
nFarthest = fc.nFarthest;
}
public FoundColors() { }
}
struct sort
{
public double dist;
public int index;
public sort(double _d, int _i) { dist = _d; index = _i; }
}
class DistComp : IComparer<sort>
{
int IComparer<sort>.Compare(sort x, sort y)
{
if ((object)x == null)
if ((object)y == null)
return 0;
else
return -1;
if ((object)y == null) return 1;
if (x.dist > y.dist) return -1;
return 1;
}
}
public class NameComp : IComparer<string>
{
int IComparer<string>.Compare(string x, string y)
{
if ((object)x == null)
if ((object)y == null)
return 0;
else
return -1;
if ((object)y == null) return 1;
return x.CompareTo(y);
}
}
public class ColorComp : IComparer<Color>
{
int IComparer<Color>.Compare(Color x, Color y)
{
if ((object)x == null)
if ((object)y == null)
return 0;
else
return -1;
if ((object)y == null) return 1;
if (x.R > y.R)
return -1;
else if (x.R < y.R)
return 1;
else if (x.G > y.G)
return -1;
else if (x.G < y.G)
return 1;
else if (x.B > y.B)
return -1;
else if (x.B < y.B)
return 1;
return 0;
}
}
}
}
And lastly some more support code, CoordinateSystem.cs:
using System;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
namespace Color_Visualizer
{
public partial class Form1 : Form
{
class CoordinateSystem
{
const int MAX = 256;
const double PlaneWidth = 600;
static Form1 Me;
static Point3D axisZ = new Point3D(0, 0, 1);
static Point3D axisY = new Point3D(0, 1, 0);
private Plane m_plane = new Plane(new Point3D(128, 128, 128), new Point3D(-128, 0, 0));
private Point3D m_pV = new Point3D(0, 0, 0);
private Point3D m_pU = new Point3D(0, 0, 0);
private double m_fInc;
public CoordinateSystem(Plane axAxis)
{
m_fInc = PlaneWidth / Me.ClientSize.Height;
Plane = axAxis;
}
public static void AssignMe(Form1 form) { Me = form; }
public Point3D U { get { return m_pU; } protected set { m_pU = value; } }
public Point3D V { get { return m_pV; } protected set { m_pV = value; } }
public Point3D Normal { get { return m_plane.Normal; } set { m_plane.Normal = value; } }
static double COSerror = 0.99619469809174553229501040247389;
public Plane Plane
{
get { return m_plane; }
set {
m_plane = value;
if (m_plane.dot(axisZ) > COSerror)
U = U.cross(m_plane, axisY);
else
U = U.cross(m_plane, axisZ);
U.div(U.norm());
V = U.cross(U, m_plane);
V.div(V.norm());
}
}
}
[DebuggerDisplayAttribute("x = {x}, y = {y}, z = {z}")]
public class Point3D
{
public double x, y, z;
public Point3D(double _x, double _y, double _z) { x = _x; y = _y; z = _z; }
public Point3D(Point3D p) { x = p.x; y = p.y; z = p.z; }
public Point3D() { x = 0; y = 0; z = 0; }
public bool Equals(Point3D p) { return x == p.x & y == p.y & z == p.z; }
public override bool Equals(object obj) { return Equals((Point3D)obj); }
public static bool operator ==(Point3D p1, Point3D p2) { return p1.Equals(p2); }
public static bool operator !=(Point3D p1, Point3D p2) { return !p1.Equals(p2); }
public static Point3D operator -(Point3D e, Point3D s) { return new Point3D(e.x - s.x, e.y - s.y, e.z - s.z); }
public static Point3D operator +(Point3D e, Point3D s) { return new Point3D(e.x + s.x, e.y + s.y, e.z + s.z); }
public static Point3D operator *(double m, Point3D v) { return new Point3D(m * v.x, m * v.y, m * v.z); }
public static Point3D operator *(Point3D v, double m) { return new Point3D(v.x / m, v.y / m, v.z / m); }
public static Point3D operator /(double m, Point3D v) { return new Point3D(m * v.x, m * v.y, m * v.z); }
public static Point3D operator /(Point3D v, double m) { return new Point3D(v.x / m, v.y / m, v.z / m); }
public static implicit operator Color(Point3D p) { return Color.FromArgb((int)p.x, (int)p.y, (int)p.z); }
public static implicit operator Point3D(Color c) { return new Point3D(c.R, c.G, c.B); }
//public override int GetHashCode()
//{
// unchecked
// {
// var hash = new SpookyHash();
// hash.Update(x);
// hash.Update(y);
// hash.Update(z);
// return hash.Final().GetHashCode();
// }
//}
// dot product (3D) which allows vector operations in arguments
public double dot(Point3D u, Point3D v) { return u.x * v.x + u.y * v.y + u.z * v.z; }
public double dot(Point3D u) { return u.x * x + u.y * y + u.z * z; }
public double norm(Point3D v) { return Math.Sqrt(dot(v, v)); } // norm = length of vector
public double norm() { return Math.Sqrt(dot(this, this)); } // norm = length of vector
public double dist(Point3D u, Point3D v) { return norm(u - v); } // distance = norm of difference
public double dist(Point3D u) { return norm(this - u); }
public Point3D cross(Point3D u, Point3D v) { return new Point3D(u.y * v.z - u.z * v.y, u.z * v.x - u.x * v.z, u.x * v.y - u.y * v.x); }
public Point3D cross(Point3D u) { return new Point3D(u.y * z - u.z * y, u.z * x - u.x * z, u.x * y - u.y * x); }
public void add(Point3D p) { x += p.x; y += p.y; z += p.z; }
public void mult(double m) { x *= m; y *= m; z *= m; }
public void div(double m) { x /= m; y /= m; z /= m; }
}
class Plane : Point3D
{
Point3D m_pNormal;
public Plane(Point3D pOrigin, Point3D pNormal) : base(pOrigin) { m_pNormal = pNormal; }
public Plane(Point3D p) : base(p) { }
public Plane(double x, double y, double z) : base(x, y, z) { }
public Point3D Normal { get { return m_pNormal; } set { m_pNormal = value; } }
public double PointToPlane(Point3D p) { return p.dot(Normal); }
}
private CoordinateSystem m_coordSys;
private CoordinateSystem Coord
{
get { return m_coordSys; }
set { m_coordSys = value; }
}
}
Only the 1st code segment is genuinely relevant to the question, but I know someone will ask for it, so I included much of the supporting code.
Note that I have also tried such things as diverse as the ProgressChanged event (after enabling it in Properties of course) and various forms of delegates. This use to be a fairly simple matter with old delegates, but I spent 10 hours today with no success, and I almost never fail to find working code examples, even if I have to wade thru dozens of inferior answers. There is an utter profusion of contradictory answers to this question depending on the date of the post and the kind of approach suggested.
The Invoke method does work if I immediately Thread.Sleep() for 200ms. Haven't tested the lower limit, but it won't be consistent across machines, so reporting it won't be informative.
Update this code
...
m_fp.Unlock();
var capturedBitmap = m_fp.Bitmap.Clone();
Invoke((MethodInvoker)delegate { pictureBox1.Image = capturedBitmap; });
...
You are probably getting the red x because you are modifying the bitmap as you assign it to the picture box. Clone the bitmap so you are not playing with the one in your picturebox.

Nullable generic object collection

public class CubicMatrix<Object?>
{
private int width;
private int height;
private int depth;
private Object[, ,] matrix;
public CubicMatrix(int inWidth, int inHeight, int inDepth)
{
width = inWidth;
height = inHeight;
depth = inDepth;
matrix = new Object[inWidth, inHeight, inDepth];
}
public void Add(Object toAdd, int x, int y, int z)
{
matrix[x, y, z] = toAdd;
}
public void Remove(int x, int y, int z)
{
matrix[x, y, z] = null;
}
public void Remove(Object toRemove)
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
for (int z = 0; z < depth; z++)
{
Object value = matrix[x, y, z];
bool match = value.Equals(toRemove);
if (match == false)
{
continue;
}
matrix[x, y, z] = null;
}
}
}
}
public IEnumerable<Object> Values
{
get
{
LinkedList<Object> allValues = new LinkedList<Object>();
foreach (Object entry in matrix)
{
allValues.AddLast(entry);
}
return allValues.AsEnumerable<Object>();
}
}
public Object this[int x, int y, int z]
{
get
{
return matrix[x, y, z];
}
}
public IEnumerable<Object> RangeInclusive(int x1, int x2, int y1, int y2, int z1, int z2)
{
LinkedList<Object> list = new LinkedList<object>();
for (int a = x1; a <= x2; a++)
{
for (int b = y1; b <= y2; b++)
{
for (int c = z1; c <= z2; c++)
{
Object toAdd = matrix[a, b, c];
list.AddLast(toAdd);
}
}
}
return list.AsEnumerable<Object>();
}
public bool Available(int x, int y, int z)
{
Object toCheck = matrix[x, y, z];
if (toCheck != null)
{
return false;
}
return true;
}
}
I've created a Cubic Matrix class in C# to store items in 3 dimensions. I need to be able to add and remove items which is why I'm using Object? (I've been led to understand that you can't use nullable generics ie. T?). However this approach gnerates an error
Type parameter declaration must be an identifier not a type
If i don't use Object? though and just use Object or T i get this error instead
Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.
What's the correct syntax and approach to use in this case?
If you want to restrict your generic type to objects only - i.e. no structs or simple types - you can add the where clause
public class CubicMatrix<T> where T : class
These means that T can only be a class.
When returning default(T) instead (as the error you are getting suggests), reference types will return null, numeric types will return 0, your custom classes will return null and nullables will return System.Nullable<T>. More about this in default Keyword in Generic Code (C# Programming Guide)
on MSDN.
I think you want to use the generic parameter T. You're making a simple container class, so allowing any generic parameter makes sense, whether it's nullable or not. To fix the error, just do what it says and use default(T) instead of null.
The error is because T could be a class or a struct, and structs can't be null. Therefore assigning a variable with type T to null is invalid. default(T) is null when T is a class and default values when T is a struct.

Categories

Resources