How can I read the properties of an object that contains an element of array type using reflection in c#. If I have a method called GetMyProperties and I determine that the object is a custom type then how can I read the properties of an array and the values within. IsCustomType is method to determine if the type is custom type or not.
public void GetMyProperties(object obj)
{
foreach (PropertyInfo pinfo in obj.GetType().GetProperties())
{
if (!Helper.IsCustomType(pinfo.PropertyType))
{
string s = pinfo.GetValue(obj, null).ToString();
propArray.Add(s);
}
else
{
object o = pinfo.GetValue(obj, null);
GetMyProperties(o);
}
}
}
The scenario is, I have an object of ArrayClass and ArrayClass has two properties:
-string Id
-DeptArray[] depts
DeptArray is another class with 2 properties:
-string code
-string value
So, this methods gets an object of ArrayClass. I want to read all the properties to top-to-bottom and store name/value pair in a dictionary/list item. I am able to do it for value, custom, enum type. I got stuck with array of objects. Not sure how to do it.
Try this code:
public static void GetMyProperties(object obj)
{
foreach (PropertyInfo pinfo in obj.GetType().GetProperties())
{
var getMethod = pinfo.GetGetMethod();
if (getMethod.ReturnType.IsArray)
{
var arrayObject = getMethod.Invoke(obj, null);
foreach (object element in (Array) arrayObject)
{
foreach (PropertyInfo arrayObjPinfo in element.GetType().GetProperties())
{
Console.WriteLine(arrayObjPinfo.Name + ":" + arrayObjPinfo.GetGetMethod().Invoke(element, null).ToString());
}
}
}
}
}
I've tested this code and it resolves arrays through reflection correctly.
You'll need to retrieve the property value object and then call GetType() on it. Then you can do something like this:
var type = pinfo.GetGetMethod().Invoke(obj, new object[0]).GetType();
if (type.IsArray)
{
Array a = (Array)obj;
foreach (object arrayVal in a)
{
// reflect on arrayVal now
var elementType = arrayVal.GetType();
}
}
FYI -- I pulled this code from a recursive object formatting method (I would use JSON serialization for it now).
Related
Hello I'm using reflection to convert object type A to equivalent object type A2, this two types has the same properties and attributes and for the conversion I'm using this rutine:
public static void CopyObject<T>(object sourceObject, ref T destObject)
{
// If either the source, or destination is null, return
if (sourceObject == null || destObject == null)
return;
// Get the type of each object
Type sourceType = sourceObject.GetType();
Type targetType = destObject.GetType();
// Loop through the source properties
foreach (PropertyInfo p in sourceType.GetProperties())
{
// Get the matching property in the destination object
PropertyInfo targetObj = targetType.GetProperty(p.Name);
// If there is none, skip
if (targetObj == null)
{
// targetObj = Activator.CreateInstance(targetType);
continue;
}
// Set the value in the destination
targetObj.SetValue(destObject, p.GetValue(sourceObject, null), null);
}
}
This works great for simple objects whith equivalent properties names, but the problem is when the soruce and taget object are of any ENUM type.
This line:
foreach (PropertyInfo p in sourceType.GetProperties())
Return no PropertyInfo object, so the loop does not run and the changes are not made, ther is no error, just not working.
So, is ther anyway to convert using reflection one object of enum type A to object of enum type A1
I know is something does not make any sense, but I need this to adapt my code to and existin application that I do not have the source code.
The idea is:
Having two enums
public enum A
{
vallue1=0,
value2=1,
value3=2
}
public enum A1
{
vallue1=0,
value2=1,
value3=2
}
and two objects of those enums types:
A prop1 {get;set;}
A1 prop2 {get;set;}
I need two get the enum value of prop1 and set that value in prop2 getting the equivalent value in the second enumeration, in a generic way (that's why I'm using reflection)?
Thanks in advance!
If you need to cast by value, you could do:
if (sourceType.IsEnum && targetType.IsEnum)
{
destObject = (T)sourceObject;
return;
}
Or by name:
if (sourceType.IsEnum && targetType.IsEnum)
{
destObject = (T)Enum.Parse(typeof(T), sourceObject.ToString());
return;
}
I have following generic method in C# that parses client data from an ASP.NET web forms application into some defined type:
public static T ParseClientRequest <T> (object data)
{
var t = (System.Collections.Generic.Dictionary<string,object>) data;
T obj = (T)Activator.CreateInstance(typeof(T));
foreach(var pair in t) {
FieldInfo field = obj.GetType().GetField(pair.Key);
field.SetValue(obj, pair.Value);
}
return obj;
}
I have two questions about it:
Is there any efficient way(using LINQ or other) of doing it without using loop? Or is it efficient enough?
The code throws exception if one of the type's field is of type other than string. How the object type can be parsed to a dynamically supplied type?
1- Efficiency is relative. Hard to answer. If it is good enough for you, then no problem
2- You can fix your code by using Convert.ChangeType
public static T ParseClientRequest<T>(object data)
{
var t = (System.Collections.Generic.Dictionary<string, object>)data;
T obj = (T)Activator.CreateInstance(typeof(T));
foreach (var pair in t)
{
FieldInfo field = obj.GetType().GetField(pair.Key);
field.SetValue(obj, Convert.ChangeType(pair.Value, field.FieldType)); //See this line
}
return obj;
}
I'm using a custom attribute to grab a property and then set it's value based on another object's value - I'm using reflection to get the attribute like this:
Class Property:
[MyPropertyName("MyString")]
string myString { get; set; }
Populating code:
public void PopulateMyPropertiesFromObject<T>(MyDataArrays dataArrays, T obj) where T : class, new()
{
Type type = typeof (T);
foreach (PropertyInfo propertyInfo in type.GetProperties())
{
foreach (MyPropertyName propName in PropertyInfo.GetCustomAttributes(true).OfType<MyPropertyName>())
{
//Get the value from the array where MyPropertyName matches array item's name property
object value = GetValue(dataArrays, propName);
//Set the value of propertyInfo for obj to the value of item matched from the array
propertyInfo.SetValue(obj, value, null);
}
}
}
I have a collection of these data arrays and so I'm looping through them instantiating a new object of type T and calling this Populate method to populate the new T for each item in the collection.
What's bugging me is how much I'm looking up the MyPropertyName custom attribute because each call to this method will be passing in the same type for obj. on Average this will happen 25 times and then the object's type will change
Is there any way I can cache the properties with their MyPropertyName attribute?Then I'd just have a list of properties + MyPropertyNames to loop through
Or can I access the attributes in a nicer way than I am?
For context: this is all happening server side of an asp.net website, I've got roughly 200-300 objects, each with around 50 properties using the attribute above for the purposes of the method above
yes you can, you can use a static Dictionary
To do so safely, a lock period is required for accessing the dictionary.
To make it thread safe.
// lock PI over process , reflectin data is collected once over all threads for performance reasons.
private static Object _pilock = new Object();
private static Dictionary<string, PropertyInfo> _propInfoDictionary;
public PropertyInfo GetProperty(string logicalKey) {
// try from dict first
PropertyInfo pi;
// lock access to static for thread safety
lock (_pilock) {
if (_propInfoDictionary.TryGetValue(logicalKey, out pi)){
return pi;
}
pi = new PropertyInfo;
// set pi ...... do whatever it takes to prepare the object before saving in dictionary
_propertyInfoDictionary.Add(logicalKey, pi);
} // end of lock period
return pi;
}
I'm maintaining part of an application which takes an entity framework entity as an object and returns a list of all its properties and corresponding values.
The method looks something like this:
public static List<string> ExtractAttributes(object data)
{
List<string> attributes = new List<string>();
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(data))
{
Console.WriteLine("Name: {0} Value: {1} Type: {2}", property.Name, property.GetValue(data), data.GetType());
attributes.Add(property.Name);
}
return attributes;
}
On calling this method with the following object:
ExtractAttributes(HashSet<Dog> dogs);
The method returns Count and Comparer (properties of HashSet) rather than properties of Dog. Therefore it is necessary to convert the data object to the first object of the hashset collection (it only needs to be the first object in the set for reasons too long to explain in detail).
The code I've written to do this is as follows:
public static List<String> ExtractAttributes(object data)
{
...
if (data.GetType().IsGenericType &&
data.GetType().GetGenericTypeDefinition() == typeof(HashSet<>))
{
List<object> hashSetAsList = new List<object>((IEnumerable<object>)data);
if (hashSetAsList.Count > 0)
{
data = hashSetAsList[0];
}
}
...
}
Is there any way to improve this ugly-looking code given the constraint of being unable to alter anything else in the method/return type?
Edit
The ExtractAttributes method is recursive (in a way which depends on the logic of some external XML) but can be represented as:
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(data))
{
Console.WriteLine("Name: {0} Value: {1} Type: {2}", property.Name, property.GetValue(data), data.GetType());
if (property.GetValue(data).GetType().IsGenericType)
{
attributes.AddRange(ExtractAttributes(property.GetValue(data)));
}
}
You can check it more generally trying to cast the object as a simple non-generic IEnumerable.
public static List<String> ExtractAttributes(object data) {
...
if (data is IEnumerable) {
var enumerator = ((IEnumerable)data).GetEnumerator();
if (enumerator.MoveNext() && enumerator.Current != null) {
data = enumerator.Current;
}
}
...
}
And if you really only want this to be done for hash-set's, I guess you can add your criteria to check to hash-set's:
if (data is IEnumerable &&
data.GetType().IsGenericType &&
data.GetType().GetGenericTypeDefinition() == typeof(HashSet<>)) {
You might consider creating an overload of ExtractAttributes method that accepts, say, IEnumerable<object>. This way, a more specific overload will be chosen when you pass HashSet.
I guess you could just cast the HashSet and take the first Element
HashSet<object> castedObject = Object as HashSet<object>;
object first = castedObject != null ? castedObject.First() : "";
I have an Object instance. In the Object's Constructor, I would like to allow user to pass in a Dictionary with which to initialize, some, if not all of the properties of the object. Now, instead of using a conditional, what I would like to do is using reflection, reflect over the properties contained in the object instance, and if the property name, maps to a key in the dictionary, update the property value with the corresponding value in the Dictionary.
In working on this, I have the following code, but it does not update the value of my object instance. Would appreciate some help getting this to work please?
public void Initialize()
{
if (Report.GlobalParameters != null)
{
PropertyInfo[] propertyInfos = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo propertyInfo in propertyInfos)
{
if (Report.GlobalParameters.ContainsKey(propertyInfo.Name))
{
Type type = this.GetType();
PropertyInfo property = type.GetProperty(propertyInfo.Name);
property.SetValue(this, Report.GlobalParameters[propertyInfo.Name], null);
}
}
}
}
First, have you attached a debugger to inspect whether or not you're getting inside the most nested if? And if you're not getting inside the most nested if, can you figure out why by comparing what you expect to be happening to what is actually happening when you inspect in the debugger?
Second, inside the most nested if, you can remove the first two lines, and replace property with propertyInfo in the third line (which will be the only line remaining when you remove the first two). You already have the PropertyInfo with the given name in hand, why are you looking it up again?
Beyond that, it looks like what you have should work. Thus, there error lies elsewhere, meaning you aren't passing in the right values, or something else that you're not telling us about is going on.
Here is a small working example of what you have that should help you:
using System;
using System.Collections.Generic;
class Foo {
public int Bar { get; set; }
public Foo(Dictionary<string, object> values) {
var propertyInfo = this.GetType().GetProperties();
foreach(var property in propertyInfo) {
if(values.ContainsKey(property.Name)) {
property.SetValue(this, values[property.Name], null);
}
}
}
}
class Program {
public static void Main(String[] args) {
Dictionary<string, object> values = new Dictionary<string, object>();
values.Add("Bar", 42);
Foo foo = new Foo(values);
Console.WriteLine(foo.Bar); // expect 42
}
}
Notice that it's exactly your logic and it works. Does this help?
Does it work any better if you switch it up and do?
public void Initialize()
{
if (Report.GlobalParameters != null)
{
foreach (KeyValuePair<string, object> kvp in Report.GlobalParameters)
{
PropertyInfo pi = this.GetType().GetProperty(kvp.Key, BindingFlags.Public | BindingFlags.Instance);
if (pi != null)
{
try
{
pi.SetValue(this, kvp.Value, null);
}
catch (Exception ex)
{
MessageBox.Show(kvp.Key + Environment.NewLine + ex.ToString(), "Error Setting Property Value");
}
}
else
{
MessageBox.Show(kvp.Key, "Property Not Found");
}
}
}
}