How to use reflection on nested classes in order to get values - c#

I have to an upper class with nested classes
public class Preferences
{
public FunctionClass function { get; set; } = new FunctionClass();
public class FunctionClass
{
public string programfolder { get; set; } = "";
...
}
public LoggerClass logger { get; set; } = new LoggerClass();
public class LoggerClass
{
public string logFolder { get; set; } = "Log";
...
}
public OptionClass options { get; set; } = new OptionClass();
public class OptionClass
{
public bool showGraphics { get; set; } = true;
...
}
public MqttSpSetupClass MqttSpSetup { get; set; } = new MqttSpSetupClass();
public class MqttSpSetupClass
{
public string strAddress { get; set; } = "localhost";
...
}
}
so I want reflection to cycle on all member of each inner class
PropertyInfo[] props_Outer = typeof(IoAppPreferences).GetProperties();
int counter = 0;
foreach (PropertyInfo prop_Upper in props_Outer)
{
var sName_Outer = prop_Upper.Name;
var val_Outer = props_Outer.GetValue(counter ++);
PropertyInfo[] properties_Inner;
switch (sName_Outer.ToUpper())
{
case "DIMS": properties_Inner = typeof(IoAppPreferences.DimsClass).GetProperties(); break;
...
}
foreach (PropertyInfo prop_Inner in properties_Inner)
{
var sName = prop_Inner.Name;
//prefs.function
var sVal = prop_Inner.GetValue(val_Outer);<------ERROR
switch (prop_Inner.Name.ToUpper())
{
...
}
}
I get an error where I put the arrow. And the reason is that val_Outer is FunctionClass function while if I hardcode prefs.function it is ok. Of course, I can put a switch per each one, but my question is: is there a better way to solve it?
I have seen this solution but can't fit to my needs

You got error because val_Outer is wrong instance. You are trying to get value out of counter integer props_Outer.GetValue(counter ++)
If your goal is to get property values from nested classes you must have instance of Preferences object:
var appPreferences = new Preferences();
var propsOuter = appPreferences.GetType().GetProperties();
foreach (var po in propsOuter)
{
var valueOuter = po.GetValue(appPreferences);
Console.WriteLine($"{po.Name}");
if (valueOuter == null) continue;
var propsInner = valueOuter.GetType().GetProperties();
foreach (var pi in propsInner)
{
var valueInner = pi.GetValue(valueOuter);
Console.WriteLine($"{pi.Name}: {valueInner}");
}
}
But getting values through reflection is pretty much useless if you already have object instance.

Related

Reflection loop propertes inside a class

I can't find a solution to my problem, so I try ask here. I have a class and I want to have a method in the class to test for the state of the Properties. The method should return true if any of the properties has more one or more values assigned. But i cannot find any examples of how to loop trough all the properties of the class itself with reflection and test if Count is > than 0.
Or should I use another technique than reflection?
I just want to avoid hard coding the Properties one more time in the Test method.
using System.Reflection;
public class cP
{
public Guid gid { get; set; } = Guid.NewGuid();
public List<string> p1 { get; set; } = new List<string>();
public List<string> p2 { get; set; } = new List<string>();
public bool HasDefinedValues()
{
List<PropertyInfo> properties = this.GetType().GetProperties().ToList();
foreach (PropertyInfo property in properties)
{
if (property.PropertyType == typeof(List<int>))
{
string PName = property.Name;
if (((List<int>)property.GetValue(property.Name, null)).Count > 0) { return true; };
}
}
return false;
}
}
This is working now 😃
using System.Reflection;
public class cP
{
public Guid gid { get; set; } = Guid.NewGuid();
public List<string> p1 { get; set; } = new List<string>();
public List<string> p2 { get; set; } = new List<string>();
public bool HasDefinedValues()
{
List<PropertyInfo> properties = this.GetType().GetProperties().ToList();
foreach (PropertyInfo property in properties)
{
if (property.PropertyType == typeof(List<string>))
{
if (((List<string>)property.GetValue(this, null)).Count > 0) { return true; };
}
}
return false;
}
}

C# Scripting with access to objects within application

Hello and still happy Ney Year
I would like to ask you for initial aid. My goal is to write a parser (e.g. source file is a bmecat-xml file and target is an Excel-file) that is dynamic and flexible enough to handle data-conversion even when sourcefile-content changes or user would require additional transformation of data.
I wrote the first part of the parser which loads data from the source-bmecat-file into corresponding classes. The class structure is exposed to the user (by reflection) and the user can map source-fields to target fields.
Where I get stuck is at the moment, when additional logic / conversion needs to be incorporated.
I think Scripting would help me to solve this. the mapping data (source field to target field) could contain an additional script that would be executed dynamically (and hence must have access to application data, especially classes which hold sourcefile and targetfile data).
It would be really great if you could point me to the right direction, to a point, where I can start from.
Thank you very much!
sample-code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace ScriptingDemoProject
{
class DataClass
{
TargetData target;
SourceData source;
MappingData map;
public DataClass()
{
target = new TargetData();
source = new SourceData();
map = new MappingData();
// generate sample data
GenerateData();
// copy source data to target data
ExecuteMapping();
}
public TargetData TargetDataInfo
{ get { return target; } }
public SourceData SourceDataInfo
{ get { return source; } }
public MappingData MappingDataInfo
{ get { return map; } }
private void GenerateData()
{
// add sourcedata
source.Header.DefaultLanguage = "deu";
source.RecipientID = "recipient...";
source.SenderID = "sender...";
SourceItem item = new SourceItem();
item.ItemID = "Item1";
item.ItemNames.AddRange( new List<SourceItemName>() {
new SourceItemName { ItemName = "Item1NameGerman", Languauge = "deu" },
new SourceItemName { ItemName = "Item1NameFrench", Languauge = "fra" }
});
source.Items.Add(item);
// add targetdata
target.AddRec(new List<TargetField>()
{
new TargetField { ColumnID=0, FieldName="ItemNo", FieldValue="Item1"},
new TargetField { ColumnID=1, FieldName="DescrGerman", FieldValue=""},
new TargetField { ColumnID=2, FieldName="DescrFrench", FieldValue=""}
});
target.AddRec(new List<TargetField>()
{
new TargetField { ColumnID=0, FieldName="ItemNo", FieldValue="Item2"},
new TargetField { ColumnID=1, FieldName="DescrGerman", FieldValue=""},
new TargetField { ColumnID=2, FieldName="DescrFrench", FieldValue=""}
});
// add mappinginstructions
map.TargetKeyFieldIndex = 0;
map.MappingFieldInfo.AddRange(new List<MappingFields>() {
new MappingFields { SourceFieldMapping="ItemName", TargetFieldMapping=1, ScriptMapping=#"... where Language=""ger""" },
new MappingFields { SourceFieldMapping="ItemName", TargetFieldMapping=2, ScriptMapping=#"... where Language=""fra""" }
});
// get properties, e.g.
var pInfo = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
}
private void ExecuteMapping()
{
// get target records
foreach (var targetRec in TargetDataInfo.TargetRecords)
{
// get key field value
string itemNo = targetRec.Where(x => x.ColumnID == map.TargetKeyFieldIndex).FirstOrDefault().FieldValue;
// get source item
SourceItem srcItem = SourceDataInfo.Items.Where(x => x.ItemID == itemNo).FirstOrDefault();
if (srcItem == null)
continue;
// get mapping instructions
foreach (var mapInstruction in map.MappingFieldInfo)
{
// i'd like to have two options
// option 1: use script
// option 2: use reflection
// option 1: script
// script will be executed at runtime and gets value from srcItem and sets value in targetRec
string script = mapInstruction.ScriptMapping;
// script would contain / execute the following statements:
TargetField field = targetRec.Where(x => x.ColumnID == mapInstruction.TargetFieldMapping).FirstOrDefault();
field.FieldValue = srcItem.ItemNames.Where(x => x.Languauge == "deu").FirstOrDefault().ItemName;
// option 2: get value by reflection
// e.g.
// though don't know how to handle List<Class>
PropertyInfo pi = SourceDataInfo.GetType().GetProperty("SenderID");
object val = pi.GetValue(SourceDataInfo, null);
// ...
}
}
}
}
public class MappingData
{
List<MappingFields> mappingFields;
public MappingData ()
{
mappingFields = new List<MappingFields>();
}
public int TargetKeyFieldIndex { get; set; }
public List<MappingFields> MappingFieldInfo
{ get { return mappingFields; } }
}
public class MappingFields
{
public string SourceFieldMapping { get; set; }
public int TargetFieldMapping { get; set; }
public string ScriptMapping { get; set; }
}
public class TargetData
{
private List<List<TargetField>> targetRecords;
public TargetData()
{
targetRecords = new List<List<TargetField>>();
}
public List<List<TargetField>> TargetRecords
{ get { return targetRecords; } }
public void AddRec(List<TargetField> TargetFields)
{
targetRecords.Add(TargetFields);
}
}
public class TargetField
{
public string FieldName
{ get; set; }
public int ColumnID
{ get; set; }
public string FieldValue
{ get; set; }
}
public class SourceData
{
private List<SourceItem> sourceItems;
private SourceHeader sourceHeader;
public SourceData()
{
sourceHeader = new SourceHeader();
sourceItems = new List<SourceItem>();
}
public SourceHeader Header
{ get { return sourceHeader; } }
public List<SourceItem> Items
{ get { return sourceItems; } }
public string SenderID
{ get; set; }
public string RecipientID
{ get; set; }
}
public class SourceHeader
{
public string DefaultLanguage
{ get; set; }
}
public class SourceItem
{
private List<SourceItemName> itemNames;
public SourceItem()
{
itemNames = new List<SourceItemName>();
}
public string ItemID
{ get; set; }
public List<SourceItemName> ItemNames
{ get { return itemNames; } }
public SourceItemName GetNameByLang(string Lang)
{
return itemNames.Where(x => x.Languauge == Lang).FirstOrDefault();
}
}
public class SourceItemName
{
public string ItemName
{ get; set; }
public string Languauge
{ get; set; }
}
}

Alternative for switch statement in c#

Can someone suggest an alternative way to solve this problem, I don't want to use SWITCH statement in my code.
Class Definition:
public class Rootobject
{
public Must[] must { get; set; }
public Should[] should { get; set; }
}
public class Should
{
public Match match { get; set; }
public Bool _bool { get; set; }
}
public class Must
{
public Match match { get; set; }
public Bool _bool { get; set; }
}
public class Match
{
public string pname { get; set; }
}
public class Bool
{
public string rname { get; set; }
}
Function Definition
public root getobject(string op)
{
Rootobject root = new Rootobject();
op ="must";
switch (op)
{
case "should":
root.should = new Should[1];
Should objShould = new Should();
objShould.match = new Match();
objShould.match.pname = "hello";
root.should[0] = objShould;
break;
case "must":
root.must = new Must[1];
Must objMust = new Must();
objMust.match = new Match();
objMust.match.pname = "hello";
root.must[0] = objMust;
break;
}
return(root);
}
Switch statement is an overhead an new type comes then i may need to add another condition. Can anyone suggest an alternative way of using switch statement.
Based on the comments under your question, I discovered it is possible to implement what #Jon Skeet stated.
You can add an Initialize Method in your RootObject class to create the dictionary (Use a ref Dictionary so to avoid setting the dictionary in your RootObject class that could change the structure of your serialization):
public void Initialize(ref Dictionary<string, Func<Rootobject>> rootDic)
{
Func<Rootobject> shouldFunc = () =>
{
Rootobject root = new Rootobject();
root.should = new Should[1];
Should objShould = new Should();
objShould.match = new Match();
objShould.match.pname = "hello";
root.should[0] = objShould;
return root;
};
Func<Rootobject> mustFunc = () =>
{
Rootobject root = new Rootobject();
root.must = new Must[1];
Must objMust = new Must();
objMust.match = new Match();
objMust.match.pname = "hello";
root.must[0] = objMust;
return root;
};
rootDic.Add("should", shouldFunc);
rootDic.Add("must", mustFunc);
}
And then call it in your getobject method like so:
public static Rootobject getobject(string op)
{
Dictionary<string, Func<Rootobject>> rootDic = new Dictionary<string,Func<Rootobject>>();
Rootobject root = new Rootobject();
root.Initialize(ref rootDic);
if(rootDic.Count > 0)
return rootDic[op].Invoke();
return new Rootobject();
}
You still going to get the same result as the solution in your question even after serializing it.

Reflections -Set objects property when the property is in a List<>

I know I can use reflection to set a objects property like this below.
public void SaveContent(string propertyName, string contentToUpdate, string corePageId)
{
var page = Session.Load<CorePage>(corePageId);
Type type = page.GetType();
PropertyInfo prop = type.GetProperty(propertyName);
prop.SetValue(page, contentToUpdate, null);
}
I'm having these classes below:
public class CorePage
{
public string BigHeader { get; set; }
public List<BigLinks> BigLinks { get; set; }
}
public class BigLinks
{
public string TextContent { get; set; }
}
My SaveContent()-method works obviously when the property to set is, for example public string BigHeader { get; set; }
But how can I do this if the the property I want to set is in the property:
public List<BigLinks> BigLinks { get; set; }
If public List<BigLinks> BigLinks { get; set; } is a list of 5 BigLinks objects, how can a set the value of, for example the third objects public string TextContent { get; set; }?
You have to get the property value using reflection and change the desired value like this:
var c = new CorePage() { BigLinks = new List<BigLinks> { new BigLinks { TextContent = "Y"}}};
var r = typeof(CorePage).GetProperty("BigLinks").GetGetMethod().Invoke(c, null) as List<BigLinks>;
r[0].TextContent = "X";
If you don't know the type of list item:
var itemInList = (typeof(CorePage).GetProperty("BigLinks").GetGetMethod().Invoke(c, null) as IList)[0];
itemInList.GetType().GetProperty("TextContent").SetValue(itemInList, "XXX", null);
Another option is casting to dynamic:
var itemInList = (typeof(CorePage).GetProperty("BigLinks").GetGetMethod().Invoke(c, null) as dynamic)[0].TextContent = "XXXTTT";

How can I correct a NullReferenceException in the following code?

I'm new to programming and this may be a simple issue but I can't seem to figure it out. I have the following 2 classes in my program:
public class TrackingObject
{
public int OrderId { get; set; }
public int ProjectCount { get; set; }
public IList<ProjectInfo> Projects { get; set; }
}
public class ProjectInfo
{
public int ProjectId { get; set; }
public string ProjectType { get; set; }
public int ImageCount { get; set; }
}
For testing I created a console application and used the following code:
static void Main(string[] args)
{
TrackingObject oTracking = new TrackingObject();
ProjectInfo pInfo = new ProjectInfo();
oTracking.OrderId = 1;
oTracking.ProjectCount = 1;
pInfo.ProjectId = 1;
pInfo.ProjectType = "CANVAS";
pInfo.ImageCount = 1;
oTracking.Projects.Add(pInfo);
Console.WriteLine(oTracking.Projects.Count);
Console.ReadLine();
}
When I run the application it throws a NullReferenceException was unhandled on the following line:
oTracking.Projects.Add(pInfo);
I understand enough to know that the exception is because oTracking.Projects is NULL. What is the correct way to accomplish what I am attempting? Any guidance will be most appreciated.
Add oTracking.Projects = new List<ProjectInfo>() before adding items to it.
Combination of I4V's answer and first comment on that answer:
public class TrackingObject
{
public int OrderId { get; set; }
public int ProjectCount { get; set; }
public IList<ProjectInfo> Projects { get; set; }
public TrackingObject() {
this.Projects = new List<ProjectInfo>();
}
//This constructor optional, but since you're using an IList, may as well
//let the user pass in an IList of a different type if they chose
public TrackingObject(IList<ProjectInfo> defaultList) {
this.Projects = defaultList;
}
}
Check if the object if null, if it is assign a new instance:
if (oTracking.Projects == null) {
oTracking.Projects = new List<ProjectInfo>();
}
Projects needs to be initialized:
oTracking.Projects = new List<ProjectInfo>();
static void Main(string[] args)
{
TrackingObject oTracking = new TrackingObject();
ProjectInfo pInfo = new ProjectInfo();
oTracking.OrderId = 1;
oTracking.ProjectCount = 1;
pInfo.ProjectId = 1;
pInfo.ProjectType = "CANVAS";
pInfo.ImageCount = 1;
oTracking.Projects = new List<ProjectInfo>();
oTracking.Projects.Add(pInfo);
Console.WriteLine(oTracking.Projects.Count);
Console.ReadLine();
}

Categories

Resources