I have the following classes:
public class Grid<T>
{
public int Width { get; }
public int Height { get; }
public float CellSize { get; }
public Vector2 GridOffset { get; }
public T[,] GridObjects { get; }
public Grid(int width, int height, float cellSize, Vector3 gridOffset, Func<Grid<T>, int, int, T> createGridObjectMethod)
{
this.Width = width;
this.Height = height;
this.CellSize = cellSize;
this.GridOffset = gridOffset;
GridObjects = new T[width, height];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
GridObjects[x, y] = createGridObjectMethod(this, x, y);
}
}
}
}
public interface IPathNode
{
PathNode GetPathNode();
}
public class PathNode : IPathNode
{
private readonly Grid<IPathNode> grid;
public int X { get; }
public int Y { get; }
public PathNode PreviousNode;
public PathNode(Grid<IPathNode> grid, int x, int y)
{
this.grid = grid;
this.X = x;
this.Y = y;
}
}
Now, I'm trying to create a Grid instance with the following line:
grid = new Grid<PathNode>(10, 10, 1, new Vector3(-5, -5, 0), (g, x, y) =>
{
Grid<IPathNode> test = (Grid<IPathNode>)g;
return new PathNode(g, x, y);
});
The problem is that PathNode asks for a Grid and my grid is made of PathNodes (or any other type that implements that interface).
How can I cast it, or instantiate it?
Tried casting the grid object, but it will fail to compile.
Also adding generics to the PathNode class but I don't like that
I ended up adding a restriction to the PathNode and the interface (and adding generics to it). I had to edit some of the code but it works. Don't really like this option since I only use the generics for it to work but yeah.
public class PathNode<T> : IPathNode<T> where T : IPathNode<T>
public interface IPathNode<T> where T : IPathNode<T>
Related
Problem Statement
There is a custom vector class:
namespace StackoverflowQuestion1
{
public class MyVector
{
public float x;
public float y;
public float z;
public MyVector(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
}
There is an interface for anything that's movable, which means positions may change:
namespace StackoverflowQuestion1
{
public interface IMovable
{
public string Name { get; }
public MyVector Position { get; }
}
}
Furniture is movable, therefore it implements the corresponding interface:
namespace StackoverflowQuestion1
{
public class Furniture : IMovable
{
public string Name { get; private set; }
public MyVector Position { get; private set; }
public Furniture(string name, float x, float y, float z)
{
this.Name = name;
this.Position = new MyVector(x, y, z);
}
}
}
Accessing the private getter of the Name is not possible, as expected. Accessing the private setter of Position is also not working, as expected. However, accessing the fields of Position is possible, as they are public.
using StackoverflowQuestion1;
class Program
{
static void Main(string[] args)
{
Furniture F = new Furniture("Chair", 1f, 2f, 3f);
F.Name = "Office chair"; // doesn't work, as expected
F.Position = new MyVector(5f, 6f, 7f); // doesn't work, as expected
F.Position.x = 5f; // works, unfortunately
F.Position.y = 6f; // works, unfortunately
F.Position.z = 7f; // works, unfortunately
}
}
Question
How to make it impossible to change the furniture's position, without making the coordinates of MyVector private and, thus, inaccesible? I want to have encapsulation, by only letting Furniture members access the position, but MyVector will become useless in other places if its values can't be changed.
A couple of points to make here:
By design you chose to make the fields public which means they are readily accessible from other classes. They are not private which is what the title implies. To force them to be read only use the readonly keyword
public class MyVector
{
public readonly float x;
public readonly float y;
public readonly float z;
public MyVector(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
Typically you won't expose the fields but instead use properties with getters defined only.
public class MyVector
{
private readonly float x;
private readonly float y;
private readonly float z;
public MyVector(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
public float X { get => x; }
public float Y { get => y; }
public float Z { get => z; }
}
Furthermore, you can simplify things using auto-properties
public class MyVector
{
public MyVector(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public float X { get; }
public float Y { get; }
public float Z { get; }
}
Finially, it recommended for value semantics where (x,y,z) will always go together to use struct declarations.
public readonly struct MyVector
{
public MyVector(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public float X { get; }
public float Y { get; }
public float Z { get; }
}
As a side note, if you try to modify the contents of a struct exposed by a property, the C# is going to complain.
Consider this code
public struct MyVector
{
public float x;
public float y;
public float z;
public MyVector(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
public class Movable
{
public Movable(MyVector position)
{
Position = position;
}
public MyVector Position { get; }
}
So even with by design allowing the contents of MyVector to be mutable (change), the compiler is going to stop you. This is because with struct types you have local copies of the data everywhere and by writing Position.x = 10f you would have modified a local copy of Position that exists in the scope where this is called, and not modified the original data.
In the question MyVector is a class and so Position.x = 10f modifies the original data and as stated this is undesirable behavior, so follow the steps above to disallow this behavior.
To make MyVector work well with other classes I often add the following functionality to such deflations. I add support for .ToString() with formatting and I add support for .Equals() (and == for structures) in order to be to write code like this:
static void Main(string[] args)
{
var pos = new MyVector(1f, 1/2f, 1/3f);
var m = new Movable(pos);
if (m.Position == pos)
{
Console.WriteLine($"{m.Position:f2}");
// (1.00,0.50,0.33)
}
}
Notice the formatting with 2 decimals and the equality check.
here is the complete code that allows this for your reference
MyVector.cs
public readonly struct MyVector : IEquatable<MyVector>, IFormattable
{
public MyVector(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public float X { get; }
public float Y { get; }
public float Z { get; }
#region IEquatable Members
/// <summary>
/// Equality overrides from <see cref="System.Object"/>
/// </summary>
/// <param name="obj">The object to compare this with</param>
/// <returns>False if object is a different type, otherwise it calls <code>Equals(MyVector)</code></returns>
public override bool Equals(object obj)
{
if (obj is MyVector other)
{
return Equals(other);
}
return false;
}
public static bool operator ==(MyVector target, MyVector other) { return target.Equals(other); }
public static bool operator !=(MyVector target, MyVector other) { return !(target == other); }
/// <summary>
/// Checks for equality among <see cref="MyVector"/> classes
/// </summary>
/// <param name="other">The other <see cref="MyVector"/> to compare it to</param>
/// <returns>True if equal</returns>
public bool Equals(MyVector other)
{
return X.Equals(other.X)
&& Y.Equals(other.Y)
&& Z.Equals(other.Z);
}
/// <summary>
/// Calculates the hash code for the <see cref="MyVector"/>
/// </summary>
/// <returns>The int hash value</returns>
public override int GetHashCode()
{
unchecked
{
int hc = -1817952719;
hc = (-1521134295) * hc + X.GetHashCode();
hc = (-1521134295) * hc + Y.GetHashCode();
hc = (-1521134295) * hc + Z.GetHashCode();
return hc;
}
}
#endregion
#region Formatting
public override string ToString() => ToString("g");
public string ToString(string formatting) => ToString(formatting, null);
public string ToString(string format, IFormatProvider provider)
{
return $"({X.ToString(format, provider)},{Y.ToString(format, provider)},{Z.ToString(format, provider)})";
}
#endregion
}
The problem with using a private setter for an object is that it only prevents you from replacing the object entirely. As it's not an immutable object, you can still access its properties change them, as you have found.
You could define an IMyVector interface with get only properties, have MyVector implement it, and then use the interface for your public Position property.
public interface IMyVector
{
float x {get;}
...
}
public class MyVector : IMyVector
{
...
}
public class Furniture : IMovable
{
public string Name { get; private set; }
public IMyVector Position { get; private set; }
...
Another design possibility is to declare IMyReadOnlyVector interface and expose it whenever we don't want to allow change vectors:
public interface IMyReadOnlyVector {
float x { get; }
float y { get; }
float z { get; }
}
public interface IMyVector : IMyReadOnlyVector {
float x { get; set; }
float y { get; set; }
float z { get; set; }
}
Then you implement MyVector:
public class MyVector : IMyVector {
public MyVector(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public float x { get; set; }
public float y { get; set; }
public float z { get; set; }
}
Now, time for the trick: IMovable uses IMyReadOnlyVector interface: we let user see Position but not allow to change it.
public interface IMovable {
string Name { get; }
// User can see position, but not allowed to change it
IMyReadOnlyVector Position { get; }
}
public class Furniture : IMovable {
// Private usage only: we don't want user explicitly change position
private MyVector m_Position;
public string Name { get; private set; }
// Public usage: user can't change vector's coordinates here
public IMyReadOnlyVector Position => m_Position;
public Furniture(string name, float x, float y, float z) {
this.Name = name;
this.m_Position = new MyVector(x, y, z);
}
// But we can change Position within the class
public void ShiftMe(int dx, int dy, int dz) {
m_Position.x += dx;
m_Position.y += dy;
m_Position.z += dz;
}
}
I'm trying to save a generic multidimensional array as json (this is not the main functionality, just the end goal) I want to keep the original location in the multidimensional array, so I can parse it back.
here's my code:
[Serializable]
public class Serializable2DArray<T>
{
public Serializable2DObject<T>[] data;
public Serializable2DArray(T[,] t)
{
List<Serializable2DObject<T>> tt = new List<Serializable2DObject<T>>();
for (int x = 0; x < t.GetLength(0); x++)
{
for (int y = 0; y < t.GetLength(1); y++)
{
Serializable2DObject<T> td = new Serializable2DObject<T>(t[x, y], x, y);
tt.Add(td);
}
}
data = tt.ToArray();
}
}
[Serializable]
public class Serializable2DObject<T>
{
public int x;
public int y;
public T obj;
public Serializable2DObject(T d, int _x, int _y)
{
obj = d;
x = _x;
y = _y;
}
}
Thank you for your assistance!
Edit:
I have this on the other end actually using the functions:
Serializable2DArray<TileData> md = new Serializable2DArray<TileData>(Mapdata);
string a = JsonUtility.ToJson(md, true);
File.WriteAllText(Application.persistentDataPath + "/map.json", a);
Tiledata Class:
[System.Serializable]
public class TileData
{
public int tileNumber;
public Vector3 position;
public int toughness;
public int health;
}
this all is returning "{ }"
and I want it to return the proper array of objects
Sorry if my question was unclear.
I have written the code below, but i see that for to access the width and the length for the last child which is badRectangle is by overriding everything inhrerited from the Rectangle and shape class, which means i have to duplicate the input and i i had 6 or more levels of inheritance the code would kind of confuse and repeat a lot of things.
This code works correctly but is the correct way of dealing with inheritance in C#.
class Program
{
static void Main(string[] args)
{
badRectangle myRect = new badRectangle(true,"Rectangle",23.0,23);
Console.WriteLine("The Area of your Rectangle = " + myRect.getArea().ToString()
+ "\nAnd " + myRect.getStatus());
Console.ReadLine();
}
public abstract class shape
{
string type;
public abstract double getArea();
public shape(string type)
{
this.type = type;
}
}
public class rectangle : shape
{
double width, length;
public rectangle(string type, double width, double length):base(type)
{
this.width = width;
this.length = length;
}
public override double getArea()
{
return width * length;
}
}
public class badRectangle : rectangle
{
double width, length;
bool badOrNot = false;
public badRectangle(bool badOrNot,string type, double width, double length):base(type,width,length)
{
this.badOrNot = badOrNot;
this.width = width;
this.length = length;
}
public string getStatus()
{
string answer = "No, Rectangle is not bad";
if (badOrNot == true)
{
answer = "Yes, Rectangle is bad";
}
return answer;
}
public override double getArea()
{
return width * length;
}
}
}
This would be the "correct" or conventional way to do this in C#:
public abstract class Shape
{
public string Type { get; private set; }
public abstract double Area { get; }
public Shape(string type)
{
this.Type = type;
}
}
public class Rectangle : Shape
{
public double Length { get; private set; }
public double Width { get; private set; }
public Rectangle(string type, double width, double length)
: base(type)
{
this.Width = width;
this.Length = length;
}
public override double Area { get { return this.Width * this.Length; } }
}
public class BadRectangle : Rectangle
{
public bool BadOrNot { get; private set; } = false;
public BadRectangle(string type, double width, double length, bool badOrNot)
: base(type, width, length)
{
this.BadOrNot = badOrNot;
}
public string Status
{
get
{
string answer = "No, Rectangle is not bad";
if (this.BadOrNot == true)
{
answer = "Yes, Rectangle is bad";
}
return answer;
}
}
}
You don't need to set width and length in the derived classes again, just pass them to the constructor of the base class. If you need to access them in the derived class, make them protected. The getArea() doesn't have to be overridden if it does the same thing.
Let me explain
I have a class X
public class X
{
private List<Y> y;
}
I have another class Y
public class Y
{
int a ;
Private List<Z> z;
}
Now i want to mannually assign some values to class Y object whitch is an array of obejcts of size 2. Now i have added the values. How can i add this to the object of class X, in which a propery of type List of Class Y exists.
Hope am able to explain it correctly.
i have
Y [] y1= new Y[2];
y1[0] = new Y();
y1[0].a=1;
y1[1]=new Y();
y1[0].a=2;
How can i assign y1[0] and y1[1] to object of X.
X x1=new X();
x1.y.add(y1[0]);
x1.y.add(y1[1]);
Its failing...
Please help me .
you can use AddRange
x1.y.AddRange(y1);
x1.y.AddRange(y1.GetRange(0,2)); // Adds first two elements
Although if its only a couple elements, its probably just as efficient to add them on their own
Since your list is private you may wish to add a method to do this
public void AddRangeToList(List<Y> items)
{
y.AddRange(items);
}
x.AddRangeToList(y1);
x.AddRangeToList(y1.GetRange(0,2));
It appears you never initialise your arrays I would make constructors to do this
public X()
{
y = new List<Y>();
}
public Y()
{
z = new List<Z>();
}
if you want to access properties of the class you need to make them public
X x1=new X();
x1.y = new List<Y>(); // need create instant of y before add items
x1.y.Add(y1[0]);
x1.y.Add(y1[1]);
I would change the classes as below
public class X
{
public List<Y> y { get; set; }
}
public class Y
{
public int a;
public List<Z> z { get; set; }
}
You should make your list property public instead of private.
Accessibility Levels (C# Reference) http://msdn.microsoft.com/en-us/library/ba0a1yw2.aspx
-
I would create a constructor that creates an instance of the List< Y>.
classes:
public class X
{
public X()
{
Y = new List<Y>();
}
public List<Y> Y { get; private set; }
}
public class Y
{
public Y()
{
Z = new List<Z>();
}
public int A { get; set; }
public List<Z> Z { get; private set; }
}
public class Z
{
public int V { get; set; }
}
Usage:
Y[] y1 = new Y[2];
y1[0] = new Y();
y1[0].A = 1;
y1[1] = new Y();
y1[1].A = 2;
X x1 = new X();
x1.Y.Add(y1[0]);
x1.Y.Add(y1[1]);
// or
X x2 = new X();
x2.Y.AddRange(y1);
// or
X x3 = new X();
foreach (Y y in y1)
x3.Y.Add(y);
i have some problem with serialization in C#
Have Code:
public struct CoOrds
{
public double x, y, z;
public CoOrds(double p1, double p2, double p3)
{
x = p1;
y = p2;
z = p3;
}
}
public struct printColor
{
public int r, g, b;
public printColor(int p1, int p2, int p3)
{
r = p1;
g = p2;
b = p3;
}
}
[Serializable]
public abstract class shape : ISerializable
{
public int borderStyle=1;
/* ===============================COLOR PARAMETERS================================ */
public printColor colorRGB = new printColor(0, 0, 0);
public System.Drawing.Drawing2D.DashStyle styleLine { get; set; }
public int widht=2;
/*=================================FILL PARAMETERS=====================================*/
public printColor fillColorRGB = new printColor(255,255, 255);
public shape()
{
}
protected shape(SerializationInfo info, StreamingContext context)
{
colorRGB.r = info.GetInt32("colorLine.r");
colorRGB.g = info.GetInt32("colorLine.g");
colorRGB.b = info.GetInt32("colorLine.b");
borderStyle = info.GetInt32("borderStyle");
fillColorRGB.r = info.GetInt32("fillColorRGB.r");
fillColorRGB.g = info.GetInt32("fillColorRGB.g");
fillColorRGB.b = info.GetInt32("fillColorRGB.b");
widht = info.GetInt32("widht");
}
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("colorLine.r",colorRGB.r);
info.AddValue("colorLine.g", colorRGB.g);
info.AddValue("colorLine.b", colorRGB.b);
info.AddValue("borderStyle",borderStyle);
info.AddValue("fillColorRGB.r", fillColorRGB.r);
info.AddValue("fillColorRGB.g", fillColorRGB.g);
info.AddValue("fillColorRGB.b", fillColorRGB.b);
info.AddValue("widht", widht);
}
.....
1. how i can serializable struct like
[Serializable]
public struct CoOrds
under the class and how the put it in shape and GetObjectData, because i have too many classes where i must use this struct
Thx
You can directly serialize it with info.AddValue("Pos", pos, typeof(CoOrds) or you can convert to/from string.
[Serializable()]
public struct Coords
{
readonly public double x, y, z;
public Coords(double x, double y, double z)
{
this.x = x;
this.y = y;
this.z = z;
}
public static Coords FromString(string value)
{
if (string.IsNullOrEmpty(value)) return new Coords();
double x = 0,y= 0,z = 0;
string[] parts = value.Split(',');
if (parts.Length > 0) double.TryParse(parts[0], out x);
if (parts.Length > 1) double.TryParse(parts[1], out y);
if (parts.Length > 2) double.TryParse(parts[2], out z);
return new Coords(x, y, z);
}
public override string ToString()
{
//Ensure round-trip formatting
return string.Format("{0:R},{1:R},{2:R}", x, y, z);
}
}
and then serialize it with
[Serializable()]
public class Vertex : ISerializable
{
public Coords pos1, pos2;
...
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Pos1", pos1.ToString());
info.AddValue("Pos2", pos2, typeof(Coords));
}
public Vertex(SerializationInfo info, StreamingContext context)
{
this.pos1 = Coords.FromString(info.GetValue("Pos1", typeof(string)) as string);
this.pos2 = (Coords)info.GetValue("Pos2", typeof(Coords));
}
}
With work equally well with the test code I did.