How to assign values to the nested objects dynamically? - c#

I'm working on a project and I need to be able to do the following:
Say, We have Aa class that is configuration class:
public class Aa
{
public Bb BbName { set; get; }
public string Dd { set; get; }
}
public class Bb
{
public string bb { set; get; }
public string cc { set; get; }
}
I want to fetch variables from environment in order to assign them to the respective properties. Here is what I mean: as a simulation of Environment.GetEnvironmentVariable, let's use Dictionary<string, string>.
Setup will look like this:
var dic = new Dictionary<string, string>();
dic.Add("Dd", "236.154");
dic.Add("BbName.bb", "value_for_Bb.bb"); // as you can see, I'm using nested object's full
dic.Add("BbName.cc", "value_for_Bb.cc"); // path, in order to be able to identify it
Then I wrote a method, which looks like this:
private static void ReadPropertiesRecursive<T>(T obj, Type type, List<string> prefixes)
{
foreach (PropertyInfo property in type.GetProperties())
{
if (property.PropertyType.GetTypeInfo().IsClass && property.PropertyType != typeof(string))
{
prefixes.Add(property.Name);
ReadPropertiesRecursive(obj, property.PropertyType, prefixes);
prefixes.Remove(property.Name);
}
else
{
var propertyFullName = prefixes != null && prefixes.Count > 0 ? $"{prefixes.Aggregate((i, j) => i + "." + j)}.{property.Name}" : property.Name;
property.SetValue(obj, dic[propertyFullName]);
Console.WriteLine(propertyFullName); // just for debugging
}
}
}
So, using recursion, I'm able to reach all nested objects of the given Type and the only thing that is left is assigning corresponding values to them. I'm trying to do that on property.SetValue(obj, dic[propertyFullName]);, but it throws an error, and it makes sense: obj is a "root" object, not the one that I want to assign values to, directly. So, I probably need to pass here not obj, but rather an instance of property, which is obj's property. Is that correct? Will that work? If yes, how should I do that?

That did the trick:
private static void ReadPropertiesRecursive<Tt>(Tt obj, Type type, List<string> prefixes)
{
foreach (PropertyInfo property in type.GetProperties())
{
if (property.PropertyType.GetTypeInfo().IsClass && property.PropertyType != typeof(string))
{
prefixes.Add(property.Name);
var val = property.GetValue(obj);
if (val == null)
val = Activator.CreateInstance(property.PropertyType);
property.SetValue(obj, val);
ReadPropertiesRecursive(val, property.PropertyType, prefixes);
prefixes.Remove(property.Name);
}
else
{
var propertyFullName = prefixes != null && prefixes.Count > 0 ? $"{prefixes.Aggregate((i, j) => i + "." + j)}.{property.Name}" : property.Name;
property.SetValue(obj, dic[propertyFullName]);
Console.WriteLine(propertyFullName);
}
}
}

Related

Iterate through Nested Classes and Transform Property, Multiple Int by 2

The goal of this code is to iterate through multiple nested classes, and multiple any integer by 2. Provided simple example, however, example will be more complicated in future.
How do I change a Object to its underlying class? When I iterate through this function, it reads the type for OuterProduct correctly, but fails for InnerProduct reading as type System.RuntimeType, giving an error below
How can I resolve this code to multiply all nested integers by 2?
An unhandled exception of type 'System.StackOverflowException' occurred in Unknown Module.
class Program
{
static void Main(string[] args)
{
var test = new OuterProduct();
test.AmountSold = 5;
test.ProductName = "BookOuter";
test.InnerProduct = new InnerProduct();
test.InnerProduct.ProductNameInner = "BookInner";
test.InnerProduct.AmountSoldInner = 7;
ReadPropertiesTest.ReadPropertiesRecursive(test);
}
}
public class OuterProduct
{
public string ProductName { get; set; }
public int AmountSold { get; set; }
public InnerProduct InnerProduct { get; set; }
}
public class InnerProduct
{
public string ProductNameInner { get; set; }
public int AmountSoldInner { get; set; }
}
public static class ReadPropertiesTest
{
public static void ReadPropertiesRecursive(object test)
{
var type = test.GetType();
foreach (PropertyInfo property in type.GetProperties())
{
if (property.PropertyType == typeof(int) || property.PropertyType == typeof(int?))
{
property.SetValue(test, (int)(property.GetValue(test)) * 2);
}
if (property.PropertyType.IsClass && !(property.PropertyType == typeof(string)))
{
ReadPropertiesRecursive(property.PropertyType);
}
}
}
}
Resources:
C#: How to get all public (both get and set) string properties of a type
How to iterate through nested properties of an object
System.RuntimeType is the implementation of the class that represents typeof(X) or something.GetType(). When you pass PropertyType to your function you are not passing the property value, but it's type.
You will need to pass the next object in the hierarchy into the recursive function by using GetValue.
Note though that this is dangerous and error prone. For example, if you have a List<> property you obviously cannot increase its Count (it is readonly!). You should check to make sure that the property can be written to using the CanWrite property.
You also need to check for null objects. On top of that we need to handle int differently from int? (otherwise casting null to int will throw). The latter we can clean up a bit with c#7 pattern matching:
public static void ReadPropertiesRecursive(object test)
{
if (test is null) // base case
return;
var type = test.GetType();
foreach (PropertyInfo property in type.GetProperties())
{
// check if we can even read the property
if(!property.CanRead)
continue;
// use pattern matching on the value
// nulls will be ignored
// we *could* cache GetValue but then it means we will invoke it for uninteresting types/properties
// it's also why I don't call GetValue until we've inspected PropertyType
if (property.CanWrite &&
(property.PropertyType == typeof(int) || property.PropertyType == typeof(int?)) &&
property.GetValue(test) is int i)
{
property.SetValue(test, i * 2);
}
else if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
{
ReadPropertiesRecursive(property.GetValue(test));
}
}
}
An alternative version that omits some of the checks against PropertyType can also be used. It's a bit cleaner looking but it could potentially perform the GetValue reflection in cases where we don't need/want it (like on a double or a struct):
public static void ReadPropertiesRecursive(object test)
{
if (test is null) // base case
return;
var type = test.GetType();
foreach (PropertyInfo property in type.GetProperties())
{
// check if we can even read the property
if(!property.CanRead)
continue;
// possibly unnecessary if not int or class
var val = property.GetValue(test);
if (property.CanWrite && val is int i)
{
property.SetValue(test, i * 2);
}
else if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
{
ReadPropertiesRecursive(val);
}
}
}
Note that you may want to have a whitelist or blacklist of types. Recursing into a Type object for example isn't going to get you much.
Alternative would be to go with more object-oriented approach. Make it responsibility of every class which need to be "updated".
For every type with properties which need to be updated introduce a method to do it.
public class OuterProduct
{
public string ProductName { get; set; }
public int AmountSold { get; set; }
public InnerProduct InnerProduct { get; set; }
public void Update()
{
AmountSold *= 2;
InnerProduct.Update();
}
}
public class InnerProduct
{
public string ProductNameInner { get; set; }
public int AmountSoldInner { get; set; }
public void Update()
{
AmountSoldInner *= 2;
}
}
// Usage is simple
var test = new OuterProduct
{
AmountSold = 5,
ProductName = "BookOuter",
InnerProduct = new InnerProduct
{
ProductNameInner = "BookInner",
AmountSoldInner = 7
}
};
test.Update();
// test.AmountSold == 10 is true
// test.InnerProduct.AmountSoldInner == 14 is true
This approach will simplify code maintenance. For example adding/removing properties or worse case scenario adding some other logic to Update method will be isolated in one class.
In your recursive call you are passing the type, not the actual property value:
if (property.PropertyType.IsClass && !(property.PropertyType == typeof(string)))
{
ReadPropertiesRecursive(property.PropertyType);
}
should be:
if (property.PropertyType.IsClass && !(property.PropertyType == typeof(string)))
{
ReadPropertiesRecursive(property.GetValue(test));
}

How can I get the value of properties in nested classes using Reflection? [duplicate]

Given the following objects:
public class Customer {
public String Name { get; set; }
public String Address { get; set; }
}
public class Invoice {
public String ID { get; set; }
public DateTime Date { get; set; }
public Customer BillTo { get; set; }
}
I'd like to use reflection to go through the Invoice to get the Name property of a Customer. Here's what I'm after, assuming this code would work:
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);
Of course, this fails since "BillTo.Address" is not a valid property of the Invoice class.
So, I tried writing a method to split the string into pieces on the period, and walk the objects looking for the final value I was interested in. It works okay, but I'm not entirely comfortable with it:
public Object GetPropValue(String name, Object obj) {
foreach (String part in name.Split('.')) {
if (obj == null) { return null; }
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
return obj;
}
Any ideas on how to improve this method, or a better way to solve this problem?
EDIT after posting, I saw a few related posts... There doesn't seem to be an answer that specifically addresses this question, however. Also, I'd still like the feedback on my implementation.
I use following method to get the values from (nested classes) properties like
"Property"
"Address.Street"
"Address.Country.Name"
public static object GetPropertyValue(object src, string propName)
{
if (src == null) throw new ArgumentException("Value cannot be null.", "src");
if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");
if(propName.Contains("."))//complex type nested
{
var temp = propName.Split(new char[] { '.' }, 2);
return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
}
else
{
var prop = src.GetType().GetProperty(propName);
return prop != null ? prop.GetValue(src, null) : null;
}
}
Here is the Fiddle: https://dotnetfiddle.net/PvKRH0
I know I'm a bit late to the party, and as others said, your implementation is fine
...for simple use cases.
However, I've developed a library that solves exactly that use case, Pather.CSharp.
It is also available as Nuget Package.
Its main class is Resolver with its Resolve method.
You pass it an object and the property path, and it will return the desired value.
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Address");
But it can also resolve more complex property paths, including array and dictionary access.
So, for example, if your Customer had multiple addresses
public class Customer {
public String Name { get; set; }
public IEnumerable<String> Addresses { get; set; }
}
you could access the second one using Addresses[1].
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Addresses[1]");
I actually think your logic is fine. Personally, I would probably change it around so you pass the object as the first parameter (which is more inline with PropertyInfo.GetValue, so less surprising).
I also would probably call it something more like GetNestedPropertyValue, to make it obvious that it searches down the property stack.
You have to access the ACTUAL object that you need to use reflection on. Here is what I mean:
Instead of this:
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);
Do this (edited based on comment):
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo");
Customer cust = (Customer)info.GetValue(inv, null);
PropertyInfo info2 = cust.GetType().GetProperty("Address");
Object val = info2.GetValue(cust, null);
Look at this post for more information:
Using reflection to set a property of a property of an object
In hopes of not sounding too late to the party, I would like to add my solution:
Definitely use recursion in this situation
public static Object GetPropValue(String name, object obj, Type type)
{
var parts = name.Split('.').ToList();
var currentPart = parts[0];
PropertyInfo info = type.GetProperty(currentPart);
if (info == null) { return null; }
if (name.IndexOf(".") > -1)
{
parts.Remove(currentPart);
return GetPropValue(String.Join(".", parts), info.GetValue(obj, null), info.PropertyType);
} else
{
return info.GetValue(obj, null).ToString();
}
}
You don't explain the source of your "discomfort," but your code basically looks sound to me.
The only thing I'd question is the error handling. You return null if the code tries to traverse through a null reference or if the property name doesn't exist. This hides errors: it's hard to know whether it returned null because there's no BillTo customer, or because you misspelled it "BilTo.Address"... or because there is a BillTo customer, and its Address is null! I'd let the method crash and burn in these cases -- just let the exception escape (or maybe wrap it in a friendlier one).
Here is another implementation that will skip a nested property if it is an enumerator and continue deeper. Properties of type string are not affected by the Enumeration Check.
public static class ReflectionMethods
{
public static bool IsNonStringEnumerable(this PropertyInfo pi)
{
return pi != null && pi.PropertyType.IsNonStringEnumerable();
}
public static bool IsNonStringEnumerable(this object instance)
{
return instance != null && instance.GetType().IsNonStringEnumerable();
}
public static bool IsNonStringEnumerable(this Type type)
{
if (type == null || type == typeof(string))
return false;
return typeof(IEnumerable).IsAssignableFrom(type);
}
public static Object GetPropValue(String name, Object obj)
{
foreach (String part in name.Split('.'))
{
if (obj == null) { return null; }
if (obj.IsNonStringEnumerable())
{
var toEnumerable = (IEnumerable)obj;
var iterator = toEnumerable.GetEnumerator();
if (!iterator.MoveNext())
{
return null;
}
obj = iterator.Current;
}
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
return obj;
}
}
based on this question and on
How to know if a PropertyInfo is a collection
by Berryl
I use this in a MVC project to dynamically Order my data by simply passing the Property to sort by
Example:
result = result.OrderBy((s) =>
{
return ReflectionMethods.GetPropValue("BookingItems.EventId", s);
}).ToList();
where BookingItems is a list of objects.
> Get Nest properties e.g., Developer.Project.Name
private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName)
{
if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0)
throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString()));
if (PropertName.Split('.').Length == 1)
return t.GetType().GetProperty(PropertName);
else
return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]);
}
if (info == null) { /* throw exception instead*/ }
I would actually throw an exception if they request a property that doesn't exist. The way you have it coded, if I call GetPropValue and it returns null, I don't know if that means the property didn't exist, or the property did exist but it's value was null.
public static string GetObjectPropertyValue(object obj, string propertyName)
{
bool propertyHasDot = propertyName.IndexOf(".") > -1;
string firstPartBeforeDot;
string nextParts = "";
if (!propertyHasDot)
firstPartBeforeDot = propertyName.ToLower();
else
{
firstPartBeforeDot = propertyName.Substring(0, propertyName.IndexOf(".")).ToLower();
nextParts = propertyName.Substring(propertyName.IndexOf(".") + 1);
}
foreach (var property in obj.GetType().GetProperties())
if (property.Name.ToLower() == firstPartBeforeDot)
if (!propertyHasDot)
if (property.GetValue(obj, null) != null)
return property.GetValue(obj, null).ToString();
else
return DefaultValue(property.GetValue(obj, null), propertyName).ToString();
else
return GetObjectPropertyValue(property.GetValue(obj, null), nextParts);
throw new Exception("Property '" + propertyName.ToString() + "' not found in object '" + obj.ToString() + "'");
}
I wanted to share my solution although it may be too late. This solution is primarily to check if the nested property exists. But it can be easily tweaked to return the property value if needed.
private static PropertyInfo _GetPropertyInfo(Type type, string propertyName)
{
//***
//*** Check if the property name is a complex nested type
//***
if (propertyName.Contains("."))
{
//***
//*** Get the first property name of the complex type
//***
var tempPropertyName = propertyName.Split(".", 2);
//***
//*** Check if the property exists in the type
//***
var prop = _GetPropertyInfo(type, tempPropertyName[0]);
if (prop != null)
{
//***
//*** Drill down to check if the nested property exists in the complex type
//***
return _GetPropertyInfo(prop.PropertyType, tempPropertyName[1]);
}
else
{
return null;
}
}
else
{
return type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
}
}
I had to refer to few posts to come up with this solution. I think this will work for multiple nested property types.
My internet connection was down when I need to solve the same problem, so I had to 're-invent the wheel':
static object GetPropertyValue(Object fromObject, string propertyName)
{
Type objectType = fromObject.GetType();
PropertyInfo propInfo = objectType.GetProperty(propertyName);
if (propInfo == null && propertyName.Contains('.'))
{
string firstProp = propertyName.Substring(0, propertyName.IndexOf('.'));
propInfo = objectType.GetProperty(firstProp);
if (propInfo == null)//property name is invalid
{
throw new ArgumentException(String.Format("Property {0} is not a valid property of {1}.", firstProp, fromObject.GetType().ToString()));
}
return GetPropertyValue(propInfo.GetValue(fromObject, null), propertyName.Substring(propertyName.IndexOf('.') + 1));
}
else
{
return propInfo.GetValue(fromObject, null);
}
}
Pretty sure this solves the problem for any string you use for property name, regardless of extent of nesting, as long as everything's a property.
Based on the original code from #jheddings, I have created a extension method version with generic type and verifications:
public static T GetPropertyValue<T>(this object sourceObject, string propertyName)
{
if (sourceObject == null) throw new ArgumentNullException(nameof(sourceObject));
if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentException(nameof(propertyName));
foreach (string currentPropertyName in propertyName.Split('.'))
{
if (string.IsNullOrWhiteSpace(currentPropertyName)) throw new InvalidOperationException($"Invalid property '{propertyName}'");
PropertyInfo propertyInfo = sourceObject.GetType().GetProperty(currentPropertyName);
if (propertyInfo == null) throw new InvalidOperationException($"Property '{currentPropertyName}' not found");
sourceObject = propertyInfo.GetValue(sourceObject);
}
return sourceObject is T result ? result : default;
}
I wrote a method that received one object type as the argument from the input and returns dictionary<string,string>
public static Dictionary<string, string> GetProperties(Type placeHolderType)
{
var result = new Dictionary<string, string>();
var properties = placeHolderType.GetProperties();
foreach (var propertyInfo in properties)
{
string name = propertyInfo.Name;
string description = GetDescriptionTitle(propertyInfo);
if (IsNonString(propertyInfo.PropertyType))
{
var list = GetProperties(propertyInfo.PropertyType);
foreach (var item in list)
{
result.Add($"{propertyInfo.PropertyType.Name}_{item.Key}", item.Value);
}
}
else
{
result.Add(name, description);
}
}
return result;
}
public static bool IsNonString(Type type)
{
if (type == null || type == typeof(string))
return false;
return typeof(IPlaceHolder).IsAssignableFrom(type);
}
private static string GetDescriptionTitle(MemberInfo memberInfo)
{
if (Attribute.GetCustomAttribute(memberInfo, typeof(DescriptionAttribute)) is DescriptionAttribute descriptionAttribute)
{
return descriptionAttribute.Description;
}
return memberInfo.Name;
}
public static object GetPropertyValue(object src, string propName)
{
if (src == null) throw new ArgumentException("Value cannot be null.", "src");
if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");
var prop = src.GetType().GetProperty(propName);
if (prop != null)
{
return prop.GetValue(src, null);
}
else
{
var props = src.GetType().GetProperties();
foreach (var property in props)
{
var propInfo = src.GetType().GetProperty(property.Name);
if (propInfo != null)
{
var propVal = propInfo.GetValue(src, null);
if (src.GetType().GetProperty(property.Name).PropertyType.IsClass)
{
return GetPropertyValue(propVal, propName);
}
return propVal;
}
}
return null;
}
usage: calling part
var emp = new Employee() { Person = new Person() { FirstName = "Ashwani" } };
var val = GetPropertyValue(emp, "FirstName");
above can search the property value at any level
Try inv.GetType().GetProperty("BillTo+Address");

How to get specific data from custom attribute on multiple properties

I'm new to attributes so what I'm trying to achieve might not be possible. From my understanding, attributes are used to bind data at compile time to specific methods, properties, classes, etc.
My attribute is very simple and defined as follows:
public class NowThatsAnAttribute : Attribute
{
public string HeresMyString { get; set; }
}
I have two properties that are using the same custom attribute like so:
public class MyClass
{
[NowThatsAnAttribute(HeresMyString = "A" )]
public string ThingyA
{
get;
set;
}
[NowThatsAnAttribute(HeresMyString = "B" )]
public string ThingyB
{
get;
set;
}
}
That is all working fine and dandy, but I am trying to access the attributes of these properties from another method and set certain data based on the attribute's value.
Here is what I am doing right now:
private void MyMethod()
{
string something = "";
var props = typeof (MyClass).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(NowThatsAnAttribute)));
//this is where things get messy
//here's sudo code of what I want to do
//I KNOW THIS IS NOT VALID SYNTAX
foreach(prop in props)
{
//my first idea was this
if(prop.attribute(NowThatsAnAttribute).HeresMyString == "A")
{
something = "A";
}
else if(prop.attribute(NowThatsAnAttribute).HeresMyString == "B")
{
something = "B";
}
//my second idea was this
if(prop.Name == "ThingyA")
{
something = "A";
}
else if(prop.Name == "ThingyB")
{
something = "B";
}
}
//do stuff with something
}
The problem is that something is always being set to "B" which I know is due to looping through the properties. I'm just unsure how to access the value associated with the attribute of a specific property. I have a feeling there's a painfully obvious way of doing this that I'm just not seeing.
I've tried using the StackFrame but ended in near the same spot I'm at now.
NOTE: Using .Net 4.0.
NOTE2: MyMethod is part of an interface which I am unable to edit so I can't pass in any parameters or anything.
Any help is appreciated.
if you pass a name of specific property, you will be able to take correct attribute value:
private string MyMethod(string propName)
{
PropertyInfo pi = typeof (MyClass).GetProperty(propName);
if (pi == null)
return null;
var a = (NowThatsAnAttribute)pi.GetCustomAttribute(typeof(NowThatsAnAttribute));
if (a!=null)
return a.HeresMyString;
return null;
}
MyMethod("ThingyA") returns A
Reflection also gives you the names of the properties.
Type t = typeof(MyClass);
PropertyInfo[] props = t.GetProperties();
foreach (PropertyInfo p in props) {
NowThatsAnAttribute attr = p.GetCustomAttributes(false)
.OfType<NowThatsAnAttribute>()
.FirstOrDefault();
if (attr != null) {
string propName = p.Name;
string attrValue = attr.HeresMyString;
switch (propName) {
case "ThingyA":
//TODO: Do something for ThingyA
break;
case "ThingyB":
//TODO: Do something for ThingyB
break;
default:
break;
}
}
}
You could also add the info to a dictionary:
var dict = new Dictionary<string, string();
...
if (attr != null) {
string propName = p.Name;
string attrValue = attr.HeresMyString;
dict.Add(propName, attrValue);
}
...
Getting value of some prop
string value = dict["ThingyA"];
You could do a generic method like this:
static object GetAttributeValue<T, A>(string attribName, string propName = "") where A : Attribute
{
object[] attribs = null;
if (string.IsNullOrEmpty(propName))
attribs = typeof(T).GetCustomAttributes(true);
else
{
PropertyInfo pi = typeof(T).GetProperty(propName);
if (pi == null)
return null;
attribs = pi.GetCustomAttributes(true);
}
A a = null;
foreach (object attrib in attribs)
{
if (attrib is A)
{
a = attrib as A;
break;
}
}
if (a != null)
{
var prop = a.GetType().GetProperty(attribName);
return prop.GetValue(a, null);
}
return null;
}
You would then call it like this:
object value = GetPropertyAttributeValue<MyClass,NowThatsAnAttribute>("HeresMyString", "ThingyA");
So your "MyMethod" could look like this:
private void MyMethod()
{
string something = "";
var props = typeof (MyClass).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(NowThatsAnAttribute)));
//this is where things get messy
//here's sudo code of what I want to do
//I KNOW THIS IS NOT VALID SYNTAX
foreach(prop in props)
{
string attribVal = object value = GetPropertyAttributeValue<MyClass,NowThatsAnAttribute>("HeresMyString", prop.Name);
//my first idea was this
if(attribVal == "A")
{
something = "A";
}
else if(attribVal == "B")
{
something = "B";
}
}
//do stuff with something
}
This would allow you to reuse the same method to get attribute values for different attributes on different properties(or attributes directly on the class by skipping the second parameter) from different classes.

Get BaseType properties with Reflection in C#

I need to list all properties for containing class, I have a lot of them, so I don't know the type in the code, but I can get it trough prop.BastType.FullName, but I can't cast it, and I don't want to change all my classes to crate "Cast" methods everywere.
To avoid printing Property Properties such as DeclaringType,ReflectedType, etc, I'm excluding them in the code, but I don't like this way...
if (("DeclaringType,ReflectedType,MetadataToken,Module,PropertyType,Attributes,CanRead" +
",CanWrite,GetMethod,SetMethod,IsSpecialName,CustomAttributes,MemberType")
.Contains(prop.Name))
continue;
full code of Method:
private static void GetAllPropertiesFor(object oo, int level, List<KeyValuePair<string,string>> result)
{
Type entType = oo.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(entType.GetProperties());
foreach (PropertyInfo prop in props)
{
object propValue = prop.GetValue(oo, null);
if (("DeclaringType,ReflectedType,MetadataToken,Module,PropertyType,Attributes,CanRead" +
",CanWrite,GetMethod,SetMethod,IsSpecialName,CustomAttributes,MemberType")
.Contains(prop.Name))
continue;
if (propValue == null)
{
result.Add(new KeyValuePair<string, string>((new string(' ', level * 2)) + prop.Name, "Object"));
//GetAllPropertiesFor(prop, level + (1*3), result);
}
else
{
if (result == null) result = new List<KeyValuePair<string, string>>();
result.Add(new KeyValuePair<string, string>((new string(' ', level)) + prop.Name, propValue.ToString()));
}
if (entType.Namespace == "System.Data.Entity.DynamicProxies" && entType.BaseType.FullName.StartsWith("RE2012.Data.Models."))
{
GetAllPropertiesFor(prop, level + 1, result);
}
// Do something with propValue
}
}
any suggestions?
this way I don't have in results, the values of:
PropertyType : System.Int32
Attributes : None
CanRead : True
CanWrite : True
just what I need.
Disclaim i can't really work out what you want so this answer is just a shot in the dark
Based on what i think i understood you want to get the Properties from a specific Class so here is my example:
First my same Inheritance
class BaseClass
{
public int Base { get; set; }
}
class ChildClass : BaseClass
{
public double Child { get; set; }
}
class ChildChildClass : ChildClass
{
public string ChildChild { get; set; }
}
now lets find the single Properties
class Program
{
static void Main(string[] args)
{
var obj = new ChildChildClass();
var ChildChildType = obj.GetType();
var p = new Program();
// here we get the ChildClass properties
var t = p.getBaseType(ChildChildType, 1);
Console.WriteLine(t.Name);
p.getproperties(t);
// here we get the BaseClass properties
t = p.getBaseType(ChildChildType, 2);
Console.WriteLine(t.Name);
p.getproperties(t);
// here we get the Object properties
t = p.getBaseType(ChildChildType, 3);
Console.WriteLine(t.Name);
p.getproperties(t);
Console.Read();
}
internal Type getBaseType(Type t, int level)
{
Type temp ;
for (int i = 0; i < level; i++)
{
temp = t.BaseType;
if(temp == null)
throw new ArgumentOutOfRangeException("you are digging to deep");
else
t = temp;
}
return t;
}
private void getproperties(Type t)
{
PropertyInfo[] properties = t.GetProperties(BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance);
foreach (PropertyInfo property in properties)
{
Console.WriteLine(property.Name);
}
Console.WriteLine("");
}
}
if you want some information about BindingFlags here is a Link

C# Reflection with recursion

I am working on the Reflection , but i am stuck while doing the recursion.
Code :
public class User {
public string Name;
public int Number;
public Address Address;
}
public class Address {
public string Street;
public string State;
public string Country;
}
now i am printing the values.
Type t = user.GetType();
PropertyInfo[] props = t.GetProperties();
foreach (PropertyInfo prp in props)
{
if(!prp.GetType().IsPrimitive && prp.GetType().IsClass)
{
// Get the values of the Inner Class.
// i am stucked over here , can anyone help me with this.
Type ty = prp.GetType();
var prpI = ty.GetProperties();
//var tp = ty.GetType().;
foreach (var propertyInfo in prpI)
{
var value = propertyInfo.GetValue(prp);
var stringValue = (value != null) ? value.ToString() : "";
console.WriteLine(prp.GetType().Name + "." + propertyInfo.Name+" Value : " +stringValue);
}
}
else
{
var value = prp.GetValue(user);
var stringValue = (value != null) ? value.ToString() : "";
console.writeline(user.GetType().Name + "." + prp.Name+" Value : " +stringValue);
}
}
i want to know how to find out whether the property is a class or the primitive. and do the recursion if it is a class.
First of all, if you want to access the properties of a type, ensure you are using a type which has properties:
public class User {
public string Name{get;set;}
public int Number{get;set;}
public Address Address{get;set;}
}
public class Address {
public string Street{get;set;}
public string State{get;set;}
public string Country{get;set;}
}
Second, prp.GetType() will always return PropertyInfo. You are looking for prp.PropertyType, which will return the Type of the property.
Also, if(!prp.GetType().IsPrimitive && prp.GetType().IsClass) won't work the way you want, because String e.g. is a class and also not a primitive. Better use prp.PropertyType.Module.ScopeName != "CommonLanguageRuntimeLibrary".
Last but not least, to use recursion, you actually have to put your code into a method.
Here's a complete example:
IEnumerable<string> GetPropertInfos(object o, string parent=null)
{
Type t = o.GetType();
PropertyInfo[] props = t.GetProperties(BindingFlags.Public|BindingFlags.Instance);
foreach (PropertyInfo prp in props)
{
if(prp.PropertyType.Module.ScopeName != "CommonLanguageRuntimeLibrary")
{
// fix me: you have to pass parent + "." + t.Name instead of t.Name if parent != null
foreach(var info in GetPropertInfos(prp.GetValue(o), t.Name))
yield return info;
}
else
{
var value = prp.GetValue(o);
var stringValue = (value != null) ? value.ToString() : "";
var info = t.Name + "." + prp.Name + ": " + stringValue;
if (String.IsNullOrWhiteSpace(parent))
yield return info;
else
yield return parent + "." + info;
}
}
}
Used like this:
var user = new User { Name = "Foo", Number = 19, Address = new Address{ Street="MyStreet", State="MyState", Country="SomeCountry" } };
foreach(var info in GetPropertInfos(user))
Console.WriteLine(info);
it will output
User.Name: Foo
User.Number: 19
User.Address.Street: MyStreet
User.Address.State: MyState
User.Address.Country: SomeCountry
First of all, avoid using reflection unless you really need it. It's slow, it's messy, it's borderline undebuggable (and I love it, but that's another thing)
If you want to dump the whole content of your object, I would recommend to transfer that to the objects themselves, they should know their inner state. You could use the objects ToString method to write their inner state, or you could define an interface for displaying the inner state
interface IStateDisplay
{
string GetInnerState();
}
and make your objects implement it. Then the code for displaying the properties will be
Console.WriteLine(user.GetInnerState());
Another option would be to use a tool like AutoMapper that hides the complexities and intricacies of reflection from you, and exposes a nice API to use.
However, if you are learning about reflection, printing the state of a complex object is a nice exercise. A few pointers in that direction:
public string Name;
is not a property, it's a field, so type.GetProperties() will not return it. Read up on what C# properties are, and how they are used and defined. A minimal property declaration is
public string Name {get; set;}
Also, prp.GetType() will return the type information for the PropertyInfo type, not for the type of the property it contains. What you need in this case is the prp.PropertyType property.
Next, I don't think the Type.IsPrimitive check is what you want it to be. That property returns true for the Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single types, and false for everything else. Most importantly typeof(string).IsPrimitive returns false.
On the same note, I don't think the Type.IsClass check is what you want it to be, either. As you are using it, that only check if the property is of a value or of a reference type, and as value types (struct) can be also very complex and contain properties and fields of their own, the check does not make sense.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace Extensions
{
public static class ObjectExtension
{
public static string ToStringProperties(this object o)
{
return o.ToStringProperties(0);
}
public static string ToStringProperties(this object o, int level)
{
StringBuilder sb = new StringBuilder();
string spacer = new String(' ', 2 * level);
if (level == 0) sb.Append(o.ToString());
sb.Append(spacer);
sb.Append("{\r\n");
foreach (PropertyInfo pi in o.GetType().GetProperties())
{
if (pi.GetIndexParameters().Length == 0)
{
sb.Append(spacer);
sb.Append(" ");
sb.Append(pi.Name);
sb.Append(" = ");
object propValue = pi.GetValue(o, null);
if (propValue == null)
{
sb.Append(" <null>");
} else {
if (IsMyOwnType(pi.PropertyType))
{
sb.Append("\r\n");
sb.Append(((object)propValue).ToStringProperties(level + 1));
} else{
sb.Append(propValue.ToString());
}
}
sb.Append("\r\n");
}
}
sb.Append(spacer);
sb.Append("}\r\n");
return sb.ToString();
}
private static bool IsMyOwnType(Type t)
{
return (t.Assembly == Assembly.GetExecutingAssembly());
}
}
}
Thanks #Sloth, you code is very useful. This is a slight modification those who are getting an "Object reference not set to an instance of an object." error. It creates an instance of the object is null and also handles arrays. More has to be done to handle all possible collection types but this is a start.
public static IEnumerable<string> GetPropertInfos(object o, string parent = null)
{
Type t = o.GetType();
// String namespaceValue = t.Namespace;
PropertyInfo[] props = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prp in props)
{
if (prp.PropertyType.Module.ScopeName != "CommonLanguageRuntimeLibrary")
{
// fix me: you have to pass parent + "." + t.Name instead of t.Name if parent != null
object value = prp.GetValue(o);
if (value == null)
{
value =
Activator.CreateInstance(Type.GetType(
(prp.PropertyType).AssemblyQualifiedName.Replace("[]", "")));
}
var propertInfos = GetPropertInfos(value, t.Name);
foreach (var info in propertInfos)
yield return info;
}
else
{
var type = GetTypeName(prp);
var info = t.Name + "." + prp.Name ;
if (String.IsNullOrWhiteSpace(parent))
yield return info;
else
yield return parent + "." + info;
}
}
}
You may use the Type.IsValueType property.

Categories

Resources