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>
The goal is to create a method which generically performs calculations on a property of a list of objects in a performant manner. Below is the entire test code:
using System;
using System.Collections.Generic;
namespace TestApp
{
public class Minute
{
public DateTime DateTimeUtc { get; set; }
public float Source { get; set; }
public float Mult2 { get; set; }
public float Mult3 { get; set; }
public float Mult4 { get; set; }
}
class Program
{
public static List<Minute> Minutes = new List<Minute>();
static void Main(string[] args)
{
for (int i = 1; i < 10000000; i++)
{
Minute newMinute = new Minute();
newMinute.Source = i;
Minutes.Add(newMinute);
}
GenerateMult2(Minutes, 2); // 160 ms
GenerateMult2Generic(Minutes, typeof(Minute), nameof(Minute.Source), nameof(Minute.Mult2),2); // 4300 ms
}
public static void GenerateMult2(List<Minute> Minutes, int multiplier)
{
for (int i = 0; i < Minutes.Count; i++)
{
// Simplified calculation, there will eventually be a lot more code that goes here!
Minutes[i].Mult2 = Minutes[i].Source * multiplier;
}
}
public static void GenerateMult2Generic<T>(List<T> SourceList, Type ContainerType, string propNameSource, string propNameMult, int multiplier)
{
var propertyInfoSource = ContainerType.GetProperty(propNameSource);
var propertyInfoMult = ContainerType.GetProperty(propNameMult);
foreach (T item in SourceList)
{
float sourceValue = (float)propertyInfoSource.GetValue(item);
propertyInfoMult.SetValue(item, sourceValue * multiplier);
}
}
}
}
In this test app there is a method called GenerateMult2, whose purpose is to make some calculation on one of the properties in a list of Minute objects. This method works fine and is fast. The problem is that the method is too specific. If I wanted to do the same calculations on the properties Mult3 and Mult4, I would need to make a separate method for each of these properties, which is too much duplicated code. I want to make this method more generic, which is, I want the method to accept lists of other types as well, for example a list of Day objects or Second objects. Furthermore, I want to tell the method which property to perform the calculations on.
So I've made an attempt at creating a generic method called GenerateMult2Generic. This method performs the exact same calculation as the GenerateMult2 method, and is multipurpose, which is what I want. The huge disadvantage is that it's way too slow due to the reflections.
How can the GenerateMult2 method be made in a generic fashion, but with a performance penalty of no more than 5%?
Update with solution
Having studied the answers here, the best is one that was given by Ed Plunkett, but somehow was removed. Therefore, I'm posting the original code updated with the ideas from that answer:
using System;
using System.Collections.Generic;
using System.Linq;
namespace TestApp
{
public class Minute : BaseTime
{
public float MovingAverageFast { get; set; }
public float MovingAverageSlow { get; set; }
public float RsiFast { get; set; }
public float RsiSlow { get; set; }
}
public class Day : BaseTime
{
public float MovingAverageFast { get; set; }
public float MovingAverageSlow { get; set; }
public float RsiFast { get; set; }
public float RsiSlow { get; set; }
}
public class BaseTime
{
public DateTime DateTimeUtc { get; set; }
public float Source { get; set; }
}
class Program
{
public static List<Minute> Minutes = new List<Minute>();
public static List<Day> Days = new List<Day>();
static void Main(string[] args)
{
Minutes = Enumerable.Range(1, 10000000).Select(n => new Minute { Source = n }).ToList();
Days = Enumerable.Range(1, 10000000).Select(n => new Day { Source = n }).ToList();
// Generating data for Minutes
GenerateMovingAverage(Minutes, 100, (m, value) => ((Minute)m).MovingAverageFast = value);
GenerateMovingAverage(Minutes, 500, (m, value) => ((Minute)m).MovingAverageSlow = value);
GenerateRsi(Minutes, 60, (m, value) => ((Minute)m).RsiFast = value);
GenerateRsi(Minutes, 250, (m, value) => ((Minute)m).RsiSlow = value);
// Generating data for Days
GenerateMovingAverage(Days, 8, (d, value) => ((Day)d).MovingAverageFast = value);
GenerateMovingAverage(Days, 45, (d, value) => ((Day)d).MovingAverageSlow = value);
GenerateRsi(Days, 5, (d, value) => ((Day)d).RsiFast = value);
GenerateRsi(Days, 21, (d, value) => ((Day)d).RsiSlow = value);
}
public static void GenerateMovingAverage(IEnumerable<BaseTime> BaseTimeObjects, int Period, Action<BaseTime, float> setter)
{
foreach (var BaseTimeObject in BaseTimeObjects)
{
float newValue;
newValue = BaseTimeObject.Source * Period; // pseudo calculation for generating moving average
setter(BaseTimeObject, newValue);
}
}
public static void GenerateRsi(IEnumerable<BaseTime> BaseTimeObjects, int Period, Action<BaseTime, float> setter)
{
foreach (var BaseTimeObject in BaseTimeObjects)
{
float newValue;
newValue = BaseTimeObject.Source / Period; // pseudo calculation for generating rsi
setter(BaseTimeObject, newValue);
}
}
}
}
The key idea here is setting the property via an Action in the caller. With this solution, the calculation methods are reused for any object and any property with good performance.
In addition to what #iSR5 wrote, you might consider using a factory design pattern, making classes that do the actual calculations. This would be good if you don't know what you actually need to do until run time.
public interface IMultiValueGenerator
{
void GenerateValue(ITimeMulti multi, int multiplier);
}
public class Multi2Generator : IMultiValueGenerator
{
public void GenerateValue(ITimeMulti multi, int multiplier)
{
multi.Mult2 = multi.Source * multiplier;
}
}
public static class MultiGeneratorFactory
{
public static IMultiValueGenerator GetGenerator(...)
{
if (condition)
return new Multi2Generator();
// etc
}
}
Not sure if I've got the full picture here, but from my understanding, you'll need to have an interface with a base class. The interface is the one that you'll use to define the object, while the base class is the container for all common operations, which can be inhered by the class children. Then, you can create child class (as many as you want) and inherit the base class. The child class will have its required properties, methods, and logic if needed.
Enough talking, let's take it in code :
interface ITimeMulti
{
DateTime DateTimeUtc { get; set; }
float Source { get; set; }
// will be used for number of available properties.
int MultCount { get; }
// the main method for generating the multipliers.
void Generate(int multNumber, int multiplier);
}
Simple ? let's now create the base class :
public class TimeMulti : ITimeMulti
{
public DateTime DateTimeUtc { get; set; }
public float Source { get; set; }
// Using Dictionary will be much faster than Reflection
protected static Dictionary<string, float> Multipliers { get; set; }
// Number of Properties (the set should be within the derived classes)
public int MultCount { get; protected set; }
// This is a restriction to create this instance from the derived classes only
private TimeMulti() { }
// for derived classes
protected TimeMulti(int multCount)
{
// Should be in this constructor only
Initiate(multCount);
}
// This is the main method to generate the multiplication part.
public void Generate(int multNumber, int multiplier)
{
if (multNumber == 0)
{
Multipliers["Mult"] = Source * multiplier;
}
else if (Multipliers.ContainsKey("Mult" + multNumber))
{
// store the value in the dictionary (this is for reference)
Multipliers["Mult" + multNumber] = SetMult(multNumber, Source * multiplier);
}
else
{
throw new NullReferenceException();
}
}
// On new instance, this will fired, which will setup the dictionary
protected void Initiate(int numberOfMultipliers)
{
// Ensure you have an active instance of the dictionary
if (Multipliers == null)
Multipliers = new Dictionary<string, float>();
// Ensurance
if(numberOfMultipliers > 0)
{
MultCount = numberOfMultipliers;
for (int x = 1; x <= numberOfMultipliers; x++)
if (!Multipliers.ContainsKey("Mult" + x))
Multipliers.Add("Mult" + x, 0);
}
else
{
throw new ArgumentOutOfRangeException();
}
}
// this is where we will replace Reflection, here is just returning the multValue
// we will override it on the derived classes
protected virtual float SetMult(int MultNumber, float multValue) => multValue;
}
Now, the derived class
public class Minute : TimeMulti
{
public float Mult1 { get; set; }
public float Mult2 { get; set; }
public float Mult3 { get; set; }
public float Mult4 { get; set; }
// MultCount = 4
public Minute(): base(4) { }
// This method will set the value of the property using switch statment, with this, you will avoid Reflection.
protected override float SetMult(int multNumber, float multValue)
{
switch (multNumber)
{
case 1:
Mult1 = multValue;
break;
case 2:
Mult2 = multValue;
break;
case 3:
Mult3 = multValue;
break;
case 4:
Mult4 = multValue;
break;
}
return multValue;
}
}
Now, you can do this :
class Program
{
// Create List with type of the ITimeMulti interface
public static List<ITimeMulti> Minutes = new List<ITimeMulti>();
static void Main(string[] args)
{
// Generate a sample
for (int i = 1; i < 10000000; i++)
Minutes.Add(new Minute() { Source = i});
// Calculate
GenerateMultipliers(Minutes, 1, 2);
}
public static void GenerateMultipliers(List<ITimeMulti> source, int multNumber, int multiplier)
{
for (int i = 0; i < source.Count; i++)
{
source[i].Generate(multNumber, multiplier);
}
}
}
If you want to create a new derived class :
public class Day : TimeMulti
{
// Properties
public float Mult1 { get; set; }
// Constructor
public Day(): base(1) { }
// This method to map the values to the properties
protected override float SetMult(int multNumber, float multValue)
{
switch (multNumber)
{
case 1:
Mult1 = multValue;
break;
}
return multValue;
}
}
This is just an example to give you a new ideas, you can do your own magic. I wouldn't go with Mult1 ...etc. I would go with a unique and a descriptive names.
Updated :
You can improve the performance of your updated code, by gathering all common properties in the base and make use of virtual and override if you want to have something override-able in a child class. Or, use interface and struct instead of classes. Also, instead of using IEnumerable use Array this would improve your performance as well.
public class BaseTime
{
// shared proprties
public DateTime DateTimeUtc { get; set; }
public float Source { get; set; }
public float MovingAverageFast { get; set; }
public float MovingAverageSlow { get; set; }
public float RsiFast { get; set; }
public float RsiSlow { get; set; }
}
public class Minute : BaseTime
{
// add your custom code for Minute
// No need for recreating them, since it's already inherited from the base
}
public class Day : BaseTime
{
// add your custom code for Day
// No need for recreating them, since it's already inherited from the base
}
class Program
{
public static BaseTime[] Minutes;
public static BaseTime[] Days;
static void Main(string[] args)
{
Minutes = Enumerable.Range(1, 10000000).Select(n => (BaseTime) new Minute { Source = n }).ToArray();
Days = Enumerable.Range(1, 10000000).Select(n => (BaseTime) new Day { Source = n }).ToArray();
// Generating data for Minutes
GenerateMovingAverage(Minutes, 100, (m, value) => m.MovingAverageFast = value);
GenerateRsi(Minutes, 60, (m, value) => m.RsiFast = value);
GenerateRsi(Minutes, 250, (m, value) => m.RsiSlow = value);
// Generating data for Days
GenerateMovingAverage(Days, 8, (d, value) => d.MovingAverageFast = value);
GenerateMovingAverage(Days, 45, (d, value) => d.MovingAverageSlow = value);
GenerateRsi(Days, 5, (d, value) => d.RsiFast = value);
GenerateRsi(Days, 21, (d, value) => d.RsiSlow = value);
}
public static void GenerateMovingAverage(BaseTime[] BaseTimeObjects, int Period, Action<BaseTime, float> setter)
{
foreach (var BaseTimeObject in BaseTimeObjects)
{
setter(BaseTimeObject, BaseTimeObject.Source * Period);
}
}
public static void GenerateRsi(BaseTime[] BaseTimeObjects, int Period, Action<BaseTime, float> setter)
{
foreach (var BaseTimeObject in BaseTimeObjects)
{
setter(BaseTimeObject, BaseTimeObject.Source / Period);
}
}
}
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 want to create a Dictionary<Coordinate, Status>, but the key is always equals to "Bot.Core.Games.Coordinate".
Classes
Coordinate
public class Coordinate
{
public int x { get; set; }
public int y { get; set; }
}
Status
public class Enums
{
public enum Status { UNCAPTURED, PLAYER1, PLAYER2, WIN }
}
First try
Dictionary<Coordinate, Status> Fields { get; set; } = new Dictionary<Coordinate, Status>()
{
{new Coordinate() { x = 0, y = 0 }, Status.UNCAPTURED}
}
Second try
I did some research and I found this: Use custom object as Dictionary Key
So the code now looks like this:
public class Coordinate
{
public int x { get; set; }
public int y { get; set; }
public bool Equals(Coordinate coordinate) => coordinate.x.Equals(x) && coordinate.y.Equals(y);
public bool Equals(object o) => Equals(o as Coordinate);
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode();
}
Third try
Since none of the previously tried code works I did more research and found this.So now the code is:
public class Coordinate
{
public int x { get; set; }
public int y { get; set; }
public class CoordinateEqualityComparer : IEqualityComparer<Coordinate>
{
public bool Equals(Coordinate a, Coordinate b) => ((a.x == b.x) & (a.y == b.y));
public int GetHashCode(Coordinate obj)
{
string combined = obj.x + "|" + obj.y;
return (combined.GetHashCode());
}
}
}
Dictionary<Coordinate, Status> Fields { get; set; } = new Dictionary<Coordinate, Status>(new Coordinate.CoordinateEqualityComparer())
{
{new Coordinate() { x = 0, y = 0 }, Status.UNCAPTURED}
}
The key is always "Bot.Core.Games.Coordinate". How to fix this?
You are missing an override in your second try:
public override bool Equals(object o)
The key is always displayed as Bot.Core.Games.Coordinate because in default, the ToString method returns the class name and this is the method the debugger calls to display its value. If you override the method like this:
public override string ToString() => $"{x} / {y}";
It will display its true value.
The problem with your third try was (as was pointed out by Camilo Terevinto and ZorgoZ) your equality comparison - try
public override bool Equals(Coordinate a, Coordinate b)
{
return ((a.x == b.x) && (a.y == b.y));
}
instead
how can access sub class properties?, I can access Y properties, in this case Name, but not x, another case is the same but instead of single reference of x, with a list of x, in this second case how iterate every object.
public class X
{
public int ID{get;set;}
public int Name{get;set;}
}
public class y
{
public string Name{get;set;}
public x Reference{get:set;}
}
//second case
public class y
{
public string Name{get;set;}
public List<x> Reference{get:set;}
}
public static void Main()
{
y classY = new y();
y.Name = "some text here";
y.x.ID = "1";
y.x.Name ="some text for x here";
}
// in another class, pass y
// so, in this method I only can get 'y' values, but not x
Hashtable table = new Hashtable();
public void GetProperties(object p)
{
Type mytype = p.GetType();
var properties = mytype.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
table.Add(property.Name, property.GetValue(p, null));
}
}
UPDATE
Also try with interface
public interface ISub
{}
public class X : ISub // etc....
if (typeof(ISub).IsAssignableFrom(property.GetType()) ) // this alwas as false
You will have to evaluate each property to know if it is a list or not:
foreach (var prop in properties)
{
var obj = prop.GetValue(p, null);
if (obj is List<x>)
{
var list = obj as List<x>;
// do something with your list
}
else
{
table.Add(prop.Name, obj);
}
}