Reference several class members - c#

I want to access three members of a class (_orderDay, _orderCustody, _orderBox) according to and indexing variable (orderIndex), using a different approach than in the following example
public class COrdering
{
private int _orderDay;
private int _orderCustody;
private int _orderBox;
public COrdering() { _orderDay = _orderCustody = _orderBox = 0; }
public int IncOrder(int orderIndex)
{
int v = orderIndex == 0 ? _orderDay : (orderIndex == 1 ? _orderCustody : _orderBox);
v++;
if (orderIndex == 0) _orderDay = v
else if (orderIndex == 1) _orderCustody = v;
else _orderBox = v;
return v;
}
}
The idea is to use less coding than in the previous example. When I coded something like this in C++ I used std::bind to create a const array of references to each field involved, but I don't know how to make something similar in C#. Can anyone help me out with this?
EDIT
I've found a way to optimize IncOrder method:
//...
private int _incDay() { return ++_orderDay; }
private int _incCustody() { return ++_orderCustody; }
private int _incBox() { return ++_orderBox; }
private IReadOnlyList<Func<int>> _funcs = Array.AsReadOnly(new Func<int>[] {_incDay, _incCustody, incBox});
public int IncOrder(int orderIndex) { return _funcs[orderIndex](); }
There may be another way, such as creating an array of references to these fields, but I don't know if that's possible.

Sounds like a job for an index operator overload:
public int this[int index] => IncOrder(index);
Usage:
COrdering ordering = new COrdering();
int newValue = ordering[0];
Updated - you can use an array internally
public class COrdering
{
public enum OrderIndex { Day = 0, Custody = 1, Box = 2, NumElements };
private readonly int[] values = new int[(int)OrderIndex.NumElements];
public int IncOrder(OrderIndex orderIndex) => ++values[(int)orderIndex];
public int this[OrderIndex index] => IncOrder(index);
}
Also, your constructor can be removed, in C# everything is auto initialized to 0 (or null for reference types).

Why not use a Dictionary<int, int>?
public class COrdering
{
Dictionary<int, int> map = new Dictionary<int, int>();
public COrdering() { map[0] = 0; map[1] = 0; map[2] = 0; }
public int IncOrder(int orderIndex)
{
return ++map[orderIndex];
}
}
In fact you can even use an int[] or a List<int>.

I understand you want to simplify your code, so in that case start by the variables where you save data, if you are accessing them by index it would make more sense to declare an array and use an enum, something like this:
public class COrdering
{
enum OrderType
{
Day = 0,
Custody = 1,
Box = 2,
Count = 3
};
private int[] _order = new int[(int)OrderType.Count];
public int IncOrder(OrderType orderIndex)
{
// Increment corresponding order type and return its value
return ++_order[(int)orderIndex];
}
}
You can see that you implement your IncOrder with just one line of code. The ++ must be before the variable name, so you get the correct answer. I either use an intermediate variable for the increment or a ++ preceded of a good comment, so that the next programmer will see it.
The other solution with [] overload is unexpected and surprising for the next guy debugging your code :-) so that being said I suppose you guess which one I'd chose.

Related

In C# is there a way to use properties with arrays?

The way I understand it, C# properties are methods that have the get and set accessors.
class MyClass
{
private int x;
public int X
{
get
{
return x;
}
set
{
x = value;
}
}
}
I can call the property of the class in a script (and its accessors) with
MyClass mc = new MyClass();
mc.X = 10;
Debug.Log(mc.X); //returns 10
To my knowledge, however, I can pass only one value to the property.
Is there a way to pass arrays? Something like
MyClass mc = new MyClass();
mc.X = new int[] { 1, 2 }; //throws an error
Debug.Log(mc.X[0]); //I'd like it to return 1
This throws an error of course. I wonder if it's possible to do it any other way.
The solution is simple - use int[] instead of int
class MyClass
{
private int[] x;
public int[] X
{
get
{
return x;
}
set
{
x = value;
}
}
}
Also you might consider using auto property instead just like this:
class MyClass
{
public int[] X { get; set; }
}
You might also want to take a look at the Lists and read some basics ;)
Sure, just make the property an array or list also:
class MyClass
{
// in general a list should never be null, but could be empty, or without values.
// thats why we initialize the field here
private List<int> x = new List<int>();
public List<int> X
{
get
{
return x;
}
set
{
x = value;
}
}
}
then you could do:
var obj = new MyClass();
obj.X.Add(3);
obj.X.Add(6);
// (or use AddRange() to add another list or array of values
// Then loop the list and output values:
foreach(int x in obj.X)
{
Console.WriteLine(x);
}
Here's a dotnetfiddle for the above code:
https://dotnetfiddle.net/T2FrQ0

Passing array by value in c#

As far as I understand, the default type or argument passing in c# is by value. Therefore no statement is required. But when I try the run following code, my A matrix in Main is being modified by the operations done to dMatrixU in the Factorize() method of class Decomposition. I'm sure the problem is in the constructor of the Decomposition when I just assing A to dMatrixU, the reference of A is being assigned instead of the values. Therefore my question on how to avoid this, all I have found is how to pass the arguments by reference. Again, as I understand no modifier is needed for passing the argument by value. Where am I wrong?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using LinearEquations;
namespace Rextester
{
public class Program
{
public static void Main(string[] args)
{
double[,] A = new double[,]
{ { 1, 1, 1 } ,
{ 4, 3, -1 } ,
{ 3, 5, 3 } };
double[] B = new double[] {1,6,4};
Decomposition lu = new Decomposition(A,B);
lu.Factorize();
PrintMatrix(A,"A:");
PrintVector(B,"B:");
PrintMatrix(lu.L,"L:");
PrintMatrix(lu.U,"U:");
PrintVector(lu.D,"D:");
}
public static void PrintMatrix(double[,] M, String Title = "Matrix: ")
{
Console.WriteLine(Title);
for(int i = 0; i<M.GetLength(0); i++)
{
for(int j = 0; j<M.GetLength(1);j++)
{
Console.Write(M[i,j]+"\t");
}
Console.Write("\n");
}
Console.Write("\n");
}
public static void PrintVector(double[] V, String Title = "Vector: ",bool AsRow = true)
{
String str = (AsRow)? "\t" : "\n";
Console.WriteLine(Title);
for(int i = 0; i<V.GetLength(0); i++)
{
Console.Write(V[i]+str);
}
Console.WriteLine("\n");
}
}
}
namespace LinearEquations
{
public class Decomposition
{
// Fields
private double[,] dMatrixA; // Parameter in A*X=B
private double[] dVectorB; // Parameter in A*X=B
private double[] dVectorX; // Result wanted in A*X=B
private double[,] dMatrixU; // A splits into L and U
private double[,] dMatrixL; // L is used to calculate D in L*D=B
private double [] dVectorD; // D is used to calculate X in U*X=D
// Properties
public double[,] A
{
get { return dMatrixA; }
set { dMatrixA = value; }
}
public double[] B
{
get { return dVectorB; }
set { dVectorB = value; }
}
public double[] X
{
get { return dVectorX; }
set { dVectorX = value; }
}
public double[,] L
{
get { return dMatrixL; }
set { dMatrixL = value; }
}
public double[,] U
{
get { return dMatrixU; }
set { dMatrixU = value; }
}
public double[] D
{
get { return dVectorD; }
set { dVectorD = value; }
}
// Constructor
public Decomposition(double[,] A, double[] B)
{
dMatrixA = A;
dVectorB = B;
dVectorX = new double[B.Length];
dMatrixU = A;
dMatrixL = new double[A.GetLength(0),A.GetLength(1)];
dVectorD = new double[B.Length];
}
// Split A into L and U
public void Factorize()
{
// Iterate per each row
for(int i = 0; i<dMatrixU.GetLength(0); i++)
{
// For all the rows make element i equals 0
for(int j = i+1; j<dMatrixU.GetLength(0);j++)
{
// Factor that assures substraction makes 0
dMatrixL[1,1] = dMatrixU[j,i] / dMatrixU[i,i];
// Iterate per each column
for(int k = 0; k<dMatrixU.GetLength(1);k++)
{
dMatrixU[j,k] = dMatrixU[j,k] - dMatrixU[i,k]*dMatrixL[1,1];
}
}
}
}
}
}
As far is i understand, the default type or argument passing in c# is by value.
Unfortunately it is a bit more complicated and also has some execptions:
Reference types like Decomposition you hand in by making a copy of the reference. Unfortunately that means both still reference the same instance in memory. So despite a copy operation, it is call-by-Reference.
With value types like Int or double and their aliases, usually a copy is made. I do not know of any case where it does not, but I was wrong on those things before. So they are call by value.
Finally String and a few other reference types are inmutable by design. That has the advantage that they behave kinda like value types in this area. You hand in a Reference, but the instance itself can not be changed. The code can only create a new instance in memory with a different value. So despite handing over literal references, it kinda works like call by value.
Your specific case
Arrays are very explicitly Reference types. Handing them into a function without side effects, requires proper cloning. If it is a array of reference types, the cloning must be deep.
In your case you have arrays of value types. If you want to avoid call-by-reference side effects, you those arrays must be cloned. However as double is a value type, this cloning can be shallow. No need for a deep clone.
Unlike Java there is not a dedicated Clone() Method. And I am not sure why exactly. However you can often use one Collection to initialize another through the constructor. Or they even have a function like Array.Copy(), as TheBatman pointed out.

Can you use a string variable to determine which function to call? Or is there a better approach to editing variables within a class?

I'm trying to create a function that takes two player classes, compares which AttackElement they used, and is able to alter each class' variables based on which AttackElement has a better advantage.
I'm sure there's a much more logical way of doing this, but at the moment I'm stuck wondering if I could concatenate a string to call the correct variable. For example, if I am trying to access a variable in a player class called WaterStrength can I simply have a string that combines the word "Water" with "Strength" in order to call the variable? I know calling functions/variables doesn't normally work that way, but in this example I'm calling this iWantToCombineThis.
int baseDamage = 2;
class PlayerClass(){
int Health = 10;
int WaterStrength = 1;
int FireStrength = 1;
}
void AnalyzeRound(PlayerClass won, PlayerClass lost, string winningElement)
{
string iWantToCombineThis = winningElement + "Strength";
lost.Health -= baseDamage * won.iWantToCombineThis;
}
AnalyzeRound(Player1,Player2,"Water");
AnalyzeRound(Player2,Player1,"Fire");
A better approach would be to make the strength type an enum:
public enum StrengthType
{
Water=1,
Fire=2
}
And then have a method on the player class to get the Strength value for a given type from a dictionary mapping strength types to int values:
private Dictionary<StrengthType, int> strengthTypes = new Dictionary<StrengthType, int>
{
[StrengthType.Water] = 12,
[StrengthType.Fire] = 15
};
public int GetStrength(StrengthType strengthType)
{
return strengthTypes[strengthType];
}
You could use a Dictionary<string, Func<PlayerClass, int>> to get the value without resorting to reflection. Try this:
int baseDamage = 2;
class PlayerClass
{
public int Health = 10;
public int WaterStrength = 1;
public int FireStrength = 1;
}
private Dictionary<string, Func<PlayerClass, int>> indirect = new Dictionary<string, Func<PlayerClass, int>>()
{
{ "WaterStrength", pc => pc.WaterStrength },
{ "FireStrength", pc => pc.FireStrength },
};
void AnalyzeRound(PlayerClass won, PlayerClass lost, string winningElement)
{
int strength = indirect[winningElement + "Strength"](won);
lost.Health -= baseDamage * strength;
}
Disclaimer: As the comments have noted, this is kind of an anti pattern and consider doing it statically.
But to answer your question, you could use reflection to do this:
public static class Utility
{
static T GetDynamicValue<T>(this Object obj, string propertyName)
{
var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
var prop = obj.GetType().GetProperty(propertyName, flags);
var val =prop.GetValue(obj);
return (T)val;
}
}
And then you can say:
var x = won.GetDynamicValue<int>("iWantToCombineThis");
lost.Health -= baseDamage * x;

Returning a reference of a struct instead of a copy on C# 3.0?

I have this code:
using System;
using System.Collections.Generic;
using UnityEngine;
public interface HaveId
{
int id { get; }
}
public struct BusinessData : HaveId
{
// business type data
public int graphic_asset_id;
public string name;
public int id { get; set; }
}
public class LookupHelper<T> where T: HaveId
{
private T[] _list;
public T[] list
{
get { return _list; }
set { _list = value; _mapToDictionary(); }
}
private Dictionary<int, int> idxById = new Dictionary<int, int>();
public LookupHelper(){}
private void _mapToDictionary()
{
if(idxById.Count > 0) idxById = new Dictionary<int, int>();
for(var z =0 ; z < list.Length; ++z)
{
idxById[list[z].id] = z;
}
}
public bool IsIdExists(int id)
{
return idxById.ContainsKey(id);
}
public T ById(int id) // is this a reference?
{
var idx = idxById[id];
if (idx >= list.Length) throw new Exception(
String.Format("Invalid Index: {0} >= {1} on {2}",idx.ToString(),list.Length.ToString(), typeof(T).Name)
);
return list[idx];
}
}
And the test code:
LookupHelper<BusinessData> bd = new LookupHelper<BusinessData>();
bd.list = new BusinessData[]
{
new BusinessData{id = 1, name = "test"},
new BusinessData{id = 2, name = "test2"},
};
bd.ById(1).name = "foo";
This give an error: "Cannot modify struct member when accessed struct is not classified as a variable"
How can I change the value of first BusinessData and keep the array still allocated on a contiguous memory (array of struct, needed for cache locality)?
This should be a simple matter of splitting it up into a few lines. Extract the object to get a copy, modify the copy, then overwrite it in the array:
BusinessData bsd = bd.ById(1);
bsd.name = "foo";
bd.SetById(1, bsd);
Of course, you'll need to write that SetById method to reinsert things into the array:
public void SetById(int id, T obj)
{
Int32 idx = idxById[id];
list[idx] = obj;
}
As you know C# borrowed something’s from C and Java. But not everything.
In C, you can create a place for struct on the stack or the heap. On the heap, I can then pass a pointer around and change the content. Very powerful.
But C# emphasizes ease of memory management via garbage collection. To make it easy, C# has the concept of boxing value types into System.Object. Additional details, can be found on Microsoft C# Programming Guide on Boxing and unboxing.
So when you access the value type in your list, you have to explicitly unbox the value. Therefore it’s a copy of the item in the list. You can do what #Nyerguds suggested.
But to make life easy, why not turn your BusinessData into a class?

List of structs - the value of the struct items is always null

i have a list of structs : user_stocks = new List<stock_Str>();
the struct is defined:
public struct stock_Str
{
public String name;
public int quote ;
public int chenge;
public bool quote_a;
public bool chenge_a;
public int[] rate_alarm;
public int[] chenge_alarm;
}
when i add item to the list i do:
stock_Str str = new stock_Str();
str.name = tbStockName.Text;
str.chenge_a = false;
str.quote_a = false;
str.quote = 0;
str.chenge = 0;
str.chenge_alarm = new int[2];
str.rate_alarm = new int[2];
for (int i = 0; i < 2; i++)
{
str.rate_alarm[i] = 0;
str.chenge_alarm[i] = 0;
}
_mainApp.user_stocks.Add(str);
my problems:
when i try to change values of the list items(of type struct), it don't change them!
my two arrays of two int's always set to null!
how can i fix it?
a: that should not be a struct, at all
b: the "value" of a struct is never null; even Nullable<T>'s null is a bit of a magic-trick with smoke, mirrors and misdirection
Re the problem;
1: yes, that is because structs have copy semantics; you have altered a copy - not the same one
2: probably the same thing
Here's a more appropriate implementation; I imagine it'll fix most of your problems:
public class Stock
{
public string Name {get;set;}
public int Quote {get;set;}
public int Chenge {get;set;}
public bool QuoteA {get;set;}
public bool ChengeA {get;set;}
public int[] RateAlarm {get;set;}
public int[] ChengeAlarm {get;set;}
}
I'm unclear what the intent of the last 2 is, but personally I would prefer a list if it is a dynamic collection, for example:
private List<int> rateAlarms;
public List<int> RateAlarms {
get { return rateAlarms ?? (rateAlarms = new List<int>()); }
}
A struct is a Value type so you never get a reference of it to do edits only the copy.
Also you should use a class here.

Categories

Resources