How to access a subclass using a string variable - c#

I have a stucture of classes and subclasses as follows:
public class Regions
{
public const string UNITEDSTATES = "United States";
public static string[] members = { UNITEDSTATES};
public static class UnitedStatesTypes
{
public const string STEEL = "steel";
public const string CONCRETE = "concrete";
public static string[] members = { STEEL, CONCRETE };
public static class SteelStandards
{
public const string A36 = "ASTM A36";
public static string[] members = { A36 };
public static class A36Grades
{
public const string GRADE_36 = "Grade 36";
public static string[] members = { GRADE_36 };
}
public static class ConcreteStandards
{
...
}
There are more values under each one of the classes, but this is just a small sample so you can get the idea of what it looks like. I am trying to create a UI to select each one of these. There are 4 dropdown menus, each menu is populated by the value of the higher menu. So if the standards dropdown is on SteelStandards, the next dropdown is populated with A36, if it was on ConcreteStandards the next would be populated with the data under ConcreteStandards. Is there a way that I can access a subclass using a string variable?
For example, the first dropdown will select United States. The next dropdown needs to piece together "UnitedStatesTypes" and then access Regions.UnitedStatesTypes.members. I have tried using braces
Regions["UnitedStatesTypes"].members
but this did not work. Is there a way to make this happen? Or is there a better way to organize my data?

You could do this with just dictionaries, albeit it gets a bit unwieldy as you go down the tree:
var regions = new Dictionary<string,Dictionary<string,Dictionary<string,Dictionary<string,List<string>>>>>();
// populate it. Yes I know how ugly that will look!
var usSteelStandards = regions["United States"]["Steel"]["Standards"];
A better way might be to refactor your code as a set of class instances, instead of trying to use static classes/members all the way. It is a typical tree structure
public class Node : IEnumerable<Node>
{
public Node(string text)
{
this.Text = text;
this.Children = new List<Node>();
}
public string Text {get; private set;}
public List<Node> Children { get; private set;}
public Node this[string childText]
{
get{ return this.Children.FirstOrDefault(x => x.Text == childText); }
}
public void Add(string text, params Node[] childNodes)
{
var node = new Node(text);
node.Children.AddRange(childNodes);
this.Children.Add(node);
}
public IEnumerator<Node> GetEnumerator()
{
return Children.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
This can then be setup and used much easier
var node = new Node("root")
{
{
"United States",
new Node("Steel")
{
{
"ASTM A36",
new Node("Grade 36")
}
},
new Node("Concrete")
{
}
}
};
Console.WriteLine(node["United States"].Children.Count);
Console.WriteLine(node["United States"]["Steel"]["ASTM A36"].Children[0].Text);
Live example: https://rextester.com/QVGN99585

Related

c# getting and setting variables within static nested classes by their string names

So i wanted to store categories of data within my "player" class with static classes so that they are named variables and are callable by name. I recently found a solution for calling variables within a class by their string name to get and set them here: C# setting/getting the class properties by string name and i tried to call the static class via string by using "Type.GetType(pattern)": Get class by string value
I attempted to modify the object to also call the static class by a string but I am missing something because get and set doesn't work at all:
public class Player
{
//categories of variables within static classes
public static class Status { public static int health = 10, stamina = 10, mana = 10; };
public static class Immunity { public static bool fire = false, water = false, giantEnemyCrab = true; };
//paralell arrays
public string[] namelistStatus = { "health", "stamina", "mana" };
public string[] namelistImmunity = { "fire", "water", "giantEnemyCrab" };
//get and set Variables from nested classes
//('classname' is the name of the class,'propertyName' is the name of the variable)
public object this[string className, string propertyName]
{
//indirectly calls variables within static classes entirely via string
get
{
//i think the problem originates from "Type.GetType(className)" not being the correct way to call the static class by their string name
Type myType = Type.GetType(className);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
return myPropInfo.GetValue(this, null);
}
set
{
Type myType = Type.GetType(className);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
myPropInfo.SetValue(this, value, null);
}
}
//display variables
public void DisplayPlayerStatus()
{
for (int i = 0; i < namelistStatus.Length; i++)
{
Console.WriteLine(namelistStatus[i]+":"+this["Status", namelistStatus[i]]);
}
for (int i = 0; i < namelistStatus.Length; i++)
{
if (Convert.ToBoolean(this["Immunity", namelistStatus[i]]))
{
Console.WriteLine(namelistStatus[i] + " Immunity");
}
}
}
}
That was a simplification of the Player class I'm trying to organize with nested static classes, there are also some functions that are meant to set the variables in the status and immunity classes but here i only made an example function for 'getting' the nested static class variables via string but 'setting' doesn't work either.
Any suggestions of how i would be able to do this properly would be much appreciated.
Problems:
Your static inner classes are just that - nested Types inside your Player class - not properties of your Player class.
Your properties inside your static class are actually Fields.
You used the wrong name list for Immunities inside Player.DisplayPlayerStatus()
You could ( but you really should NOT , reconsider your design ) fix it like this
public class Player
{
public string[] namelistImmunity = { "fire", "water", "giantEnemyCrab" };
public string[] namelistStatus = { "health", "stamina", "mana" };
public object this[string className, string propertyName]
{
//indirectly calls variables within static classes entirely via string
get
{
var innerClass = GetType().GetNestedType(className);
var myFieldInfo = innerClass?.GetField(propertyName);
return myFieldInfo.GetValue(null);
}
set
{
var innerClass = GetType().GetNestedType(className);
var myFieldInfo = innerClass?.GetField(propertyName);
myFieldInfo?.SetValue(null, value);
}
}
public void DisplayPlayerStatus()
{
// why for and indexing - foreach is far easier
foreach (var s in namelistStatus)
Console.WriteLine(s + ":" + this["Status", s]);
// you used the wrong list of names here
foreach (var n in namelistImmunity)
if (Convert.ToBoolean(this["Immunity", n]))
Console.WriteLine(n + " Immunity");
}
public static class Immunity
{
// these are fields
public static bool fire = false,
water = false,
giantEnemyCrab = true;
};
public static class Status
{
// fields as well
public static int health = 10,
stamina = 10,
mana = 10;
};
}
Tested with:
using System;
internal class Program
{
public static void Main(string[] args)
{
Player p = new Player();
p.DisplayPlayerStatus();
Console.WriteLine();
p["Status", "health"] = 200;
p.DisplayPlayerStatus();
Console.ReadLine();
}
// the fixed stuff goes here
}
Output:
health:10
stamina:10
mana:10
giantEnemyCrab Immunity
health:200
stamina:10
mana:10
giantEnemyCrab Immunity
Your classes are currently very tighty coupled, if you add psyonicPower to your stats, you need to edit your Player, edit Player.Status, modify Player.namelistStatus.
You could autodiscover the static class fieldnames via reflection in a similar fashion like you access the field-values and get rid of both your namelistXXXX - but still, the design is bad.

Getting the Total Number of Elements From All Lists Belonging to An Object

I have been given some code that has objects composed of lists of different types. A simple example of what I mean:
public class Account
{
private long accountID;
private List<string> accountHolders;
private List<string> phoneNumbers;
private List<string> addresses;
public Account()
{
this.accountHolders = new List<string>();
this.phoneNumbers = new List<string>();
this.addresses = new List<string>();
}
public long AccountID
{
get
{
return this.accountID;
}
set
{
this.accountID = value;
}
}
}
For a requirement I need to get the total amount of elements in each list for validation purposes. I have the following method which works:
public class AccountParser
{
// Some code
public int CountElements(Account acct)
{
int count = 0;
count += acct.accountHolders.Count();
count += acct.phoneNumbers.Count();
count += acct.addresses.Count();
return count;
}
}
but was wondering if there was a better way to do this. I know I can enumerate over a List with Linq but I can't seem to get it to work in this case.
What you're doing is the right thing
You could do it in one line without declaring any variable
public int CountElements(Account acct)
{
return acct.accountHolders.Count() + acct.phoneNumbers.Count() + acct.addresses.Count();
}
But it doesn't change much.
The ammount of lists is static, because the class is static, so it doesn't make sense to use Reflection if the structure wont change.
Now you could have more than one Account classes with different types of lists. In that case, i would create an abstract AbsAccount class, that has an abstract CountElements property:
public abstract class AbsAccount
{
public abstract int CountElements { get; }
}
public class Account: AbsAccount
{
private List<string> accountHolders;
private List<string> phoneNumbers;
private List<string> addresses;
public override int CountElements
{
get
{
return this.accountHolders.Count()
+ this.phoneNumbers.Count()
+ this.addresses.Count();
}
}
}
public class AccountParser
{
// Some code
public int CountElements(AbsAccount acct)
{
return acct.CountElements;
}
}
But maybe im taking it too far...
You can add items to a list then call .Summethod on it, but it's not better from performance point of view.
public class AccountParser
{
// Some code
public int CountElements(Account acct)
{
List<string> all = new List<string>();
all.AddRange(acct.accountHolders);
all.AddRange(acct.phoneNumbers);
all.AddRange(acct.addresses);
return all.Count();
}
}
Another approach will be (because I can see you are not exposing directly your lists) to use observer pattern, and update the number of elements in another field or even list, every time you are updating one of your lists. Then get the value from that field, but I think the best way is the one you have already adopted.

Assign reference to list properly c#

I'm trying to add strings to a List<string> so I can print them with a loop in a certain point of time, being more specific here is part of my code:
public class Foo{
public string propertyA;
public string propertyB;
public string propertyC;
public List<string> list;
Public Foo(){
list = new List<string>();
list.Add(propertyA);
list.Add(propertyB);
list.Add(propertyC);
}
}
In later code, after assigning propertyA and the other variables and trying to iterate over the List I get empty strings. I require the properties to be in the list. My questions is which would be the best way to achieve this?
Looks like you are getting empty strings because when you are adding to the list the values in your properties have not been set at the time that the Foo() constructor is called...
Try passing values and setting them in the Foo constructor as follows:
public class Foo{
public string propertyA;
public string propertyB;
public string propertyC;
public List<string> list;
Public Foo(string propA, string propB, string propC){
propertyA = propA;
propertyB = propB;
propertyC = propC;
list = new List<string>();
list.Add(propertyA);
list.Add(propertyB);
list.Add(propertyC);
}
}
Alternatively you could add the values to the list at a later time when the properties are actually set and not in the constructor e.g.
public string PropertyA
{
//set the person name
set { propertyA = value;
list.Add(value);
}
//get the person name
get { return propertyA; }
}
...
What you're seeing is expected behavior. Updating "propertyA", etc later on won't update the strings that have already been added to the collection.
You could consider using a Dictionary instead of your own class, and then adding and updating elements is easier: (and you don't have to keep updating your class with new property names)
var properties = new Dictionary<string, string>();
properties.Add("propertyA", "some value of property A");
properties["propertyA"] = "some new value";
And when you want to display the values later:
MessageBox.Show(string.Join(Environment.NewLine, properties));
Alternatively, if you want a class and the option of adding properties to it, then maybe extending the Dictionary class like this will at least make things easier to maintain, so you can add more properties that'll stay in sync with the underlying Dictionary, with a minimum of fuss.
public class PropertyCollection : Dictionary<string, string>
{
public string PropertyA
{
get { return GetValue(); }
set { StoreValue(value); }
}
public string PropertyB
{
get { return GetValue(); }
set { StoreValue(value); }
}
protected string GetValue([CallerMemberName] string propName = "")
{
if (ContainsKey(propName))
return this[propName];
return "";
}
protected void StoreValue(string propValue, [CallerMemberName] string propName = "")
{
if (ContainsKey(propName))
this[propName] = propValue;
else
Add(propName, propValue);
}
}
If you want to assign propertyA, B, C after an instance of Foo is created and enumerate them, you could try something like this:
public class Foo
{
public string propertyA { get { return list[0]; } set { list[0] = value; } }
public string propertyB { get { return list[1]; } set { list[1] = value; } }
public string propertyC { get { return list[2]; } set { list[2] = value; } }
public List<string> list = new List<string>() {"", "", ""};
}
For the reasons why the code behaves in a way you might not expect, see How are strings passed in .NET?

Create a method which return a list of parameters (const string) from a class

I'm developing a class which contains some const strings
public static class Constants
{
public const string CarID= "car_id";
//public const string NumberID= "number_id"; // this is the second const string might be added, so
//the new created function can return the two
}
public class CarENParameters
{
public string Params { get; set; }
public CarENParameters(string carId)
{
Params = carId;
}
}
public static class CarPropertyProcess
{
//test params
public static CarENProps Parse(Uri uri,string content)
{
string carID= Regex.Matches(content, #"\$\('#CarXL'\)\.val\((\d+)\)", RegexOptions.None)[0].Groups[1].Value;
var parameters = new Dictionary<string, string>
{
{Constants.CarID, carID},
};
return new CarENProps(uri.AbsoluteUri, parameters);
}
public static CarENParameters GetParameters()
{
return new CarENParameters(Constants.CarID);
}
}
In the class Constants, I have one carID, now the case is it might have more than one const string like : public const string NumberID= "number_id";
So I want to create one function to return a list of those const strings, which are car_id and number_id with a class name CarENParameters but I havent figured out how to return a list by a get/set in a class, should I use dictionary or keyvaluespair to achieve that ? I'm quite new to C# so hope that I can have a better point of view from the helps of you guys. Thanks
Are you looking for something like this:
public static List<CarENParameters> GetParameters()
{
return new List<CarENParameters>()
{
new CarENParameters(Constants.CarID1),
new CarENParameters(Constants.CarID2),
new CarENParameters(Constants.CarID3)
}
}
You can use reflection for this
don't forget to put using System.Reflection;
// get class type
Type type = typeof(Constants);
// get a list of fields
FieldInfo[] fields = type.GetFields();
List<CarENParameters> list = new List<CarENParameters>();
// loop on field list
foreach (FieldInfo field in fields)
{
// if field is a string add it to our return list
if (field.FieldType == typeof(String))
list.Add(new CarENParameters((String) field.GetValue(null)));
}

C# problem with object array

I am fairly new to arrays in C# and am used to storing a mass of data in a string and in INI files and then breaking it down into basic arrays using delimiters...so yeh, my knowledge is almost none existent.
My main form class begin this definition:
public CAirportData[] _AirportData; //size not known
This is the method I am using to create the array:
...string[] airports = possibleAirports.Split(','); //size is known
foreach (string airport in airports)
{
string[] rwys = inif.Read(airport, "rwys").Split(':'); //size is known (2)
_AirportData = new CAirportData[] { new CAirportData() { icao=airport, depRwy=rwys[0], arrRwy=rwys[1] } };
}
I know this just boils down to my limited knowledge of objects and arrays. But I can't seem to find anything on the internet that uses this sort of thing. I have tried to combine other peoples code with little success.
I need the _AirportData array to be available outside of the form hence public and declared outside of any methods. I supose the main problem is that I am overwriting array and foreach airport I am creating a new array hence loosing the previous. I had tried moving the ..= new CAirportData[] to all sorts of places but Visual Studio doesn't like it.
Below is the class definition for CAirportData:
public class CAirportData
{
public string icao { get; set; }
public string depRwy { get; set; }
public string arrRwy { get; set; }
public override string ToString()
{
string result = string.Format("ICAO: {0}, Dep: {1}, Arr: {2}", this.icao, this.depRwy, this.arrRwy);
return result;
}
}
public class CMRunways
{
public string icao { get; set; }
public string depRwy { get; set; }
public string arrRwy { get; set; }
}
Many thanks in advance for any help!
What you're looking for is generic List. Change the definition to:
public List<CAirportData> _AirportData = new List<CAirportData>();
Then the code in the loop to:
_AirportData.Add(new CAirportData { icao=airport, depRwy=rwys[0], arrRwy=rwys[1] });
This is what I would do...Create a static class, with a static property (airports) and add a static constructor to load the airports from file at the begining.
public static class Session
{
public static CAirportData[] _AirportData;
static Session()
{
string airports = possibleAirports.Split(",");
foreach (string airport in airports)
{
string[] rwys = inif.Read(airport, "rwys").Split(':'); //size is known (2)
_AirportData = new CAirportData[] { new CAirportData() { icao=airport, depRwy=rwys[0], arrRwy=rwys[1] } };
}
}
}
Now you can access the array anywhere in the project like
MessageBox.Show(Session.CAirportData[0].depRwy);

Categories

Resources