I've never worked with Reflection but I have a situation where I think it would be useful without being too expensive as a process. I have five different List<.object> where each one has different properties and I have a WebMethod that returns only one of these types of List<.object>'s based on a switch statement and passes the resulting list to a method that should iterate through the values and write them out to a StringBuilder that is passed back to the WebMethod. I did not want to have to write five different methods to iterate through each object and write out the string.
Looking through SO resulted in this code as a good start for the method (below) however, I don't understand the best way to get what I need after that. I tried using PropertyInfo[] but it threw errors while casting the object. IEnumerable doesn't throw errors but I can't seem to access the values either.
private StringBuilder generateString<T>(T obj) where T : class
{
//Trying to use PropertyInfo[] example
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach(PropertyInfo p in properties)
sb.Append("{0},{1}", p.Name, p.GetValue(obj,null));
//Trying to use IEnumerable example
IEnumerable enumerable = (IEnumerable)obj;
foreach(var x in enumerable)
sb.Append(x);
}
When I try to look at the values in the PropertyInfo[] array it shows me that it has three items, the last being my object type but I can't seem to find any of the actual values. It throws two errors TargetParameterCountException and TargetInvocationException.
However, when I look at the IEnumerable data of x it shows me what I expect to see except not in a format where I think can access the individual pieces, for example:
x
{Base.DataType.MyObject}
Age: 24
Name: Helen Smith
Cost: 24.00
Date: 2/25/2012
Which is the correct way to try and access this data? And how do I do it correctly?
It seems that you are passing an IEnumerable, not a single object.If so you need to iterate over the objects and get the type of each item, and properties separately:
if(obj is IEnumerable)
{
foreach(var item in (IEnumerable)obj)
{
Type type = item.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach(PropertyInfo p in properties)
sb.Append("{0},{1}", p.Name, p.GetValue(item,null));
}
}
What exactly do you mean by which is the best way to access the data? Are you just trying to access what is in the StringBuilder object or are you just needing to output the information in some form?
The way I have used reflection in the past to get the name and the value would be like this:
public StringBuilder generateString(List<object> objList)
{
StringBuilder sb = new StringBuilder();
int itemNumber = 0;
foreach(var obj in objList)
{
foreach (var prop in obj.GetType().GetProperties())
{
// i used the new line characters to increase readability for
// output to a console so just ignore them.
sb.Append("Item " + itemNumber);
sb.Append("\n");
sb.Append(prop.Name + ", " + prop.GetValue(obj, null));
sb.Append("\n");
}
}
return sb;
}
Viewing the output of this string builder object i saw this:
Item 1
Property1, value
Property2, value
Property3, value
Item 2
Property1, value
Property2, value
Property3, value
etc...
I hope this helped you.
If all the objects have the same type, which I believe is what you are doing. And you are passing in a List<T>, you can get the item type like so:
var listType = obj.GetType();
var itemType = listType.GetElementType();
That would give you itemType of MyObject if you passed in a List<MyObject>. From there you should be able to GetProperties as you were.
If the list is heterogeneous, then you will need to loop through and get the properties of each item as in #Selman22's answer.
Related
I have been trying to set some field values of specific objects in C#.
For other reasons I need to construct a List of values from a string then I want to assign this to a field in an object.
The way I indicate the value type of the list is something like this in string
name:type{listItemName:listItemType{listItemValue}}
This list can be of any type, so it is undetermined until we reach conversion.
I am using
List<dynamic> ldy = new List<dynamic>();
foreach (string listElement in listElements)
{
if (listElement == "") continue;
Type leDataType = GetDataType(listElement);
string leData = GetDataRaw(listElement);
var leDynamic = ConstructDataObject(leDataType, leData, listElement);
ldy.Add(leDynamic);
}
Which ends up with the correct data and with the correct data type when I enquire, however when I am trying to use the resulting ldy list and assign it to a field, of course it acts as a List<object>
thus not allowing me to assign.
Finally so, I am using field.SetValue(targetObject, ldy); to assign the value to the field.
The error message I am getting is
ArgumentException: Object of type 'System.Collections.Generic.List`1[System.Object]' cannot be converted to type 'System.Collections.Generic.List`1[System.Int32]'.
Which to be clear, I do understand and I do understand why, however I dont really see how could I solve this issue, not by solving this nor by changing the fundaments of my code design.
Please help!
As #juharr suggested I have searched for solutions to do this with reflection.
Here is my solution:
private static IList GetTypedList(Type t)
{
var listType = typeof(List<>);
var constructedListType = listType.MakeGenericType(t);
var instance = Activator.CreateInstance(constructedListType);
return (IList)instance;
}
I have some class that contains a List with a type of PropertyInfo:
public List<PropertyInfo> Properties {get; set;}
And I have an instance of that class, but it is not known which class it is at the run-time. So with that instance, in my case it's called P, I need to loop through the upper-mentioned list. Now I can get the value of the property by using:
var PropList = P.GetType().GetProperty("Properties").GetValue(this, null)
But if I try to loop through it:
foreach (var thisProp in PropList) - I get an error.
So, is there any other way to do this?
Thanks
You have to cast:
var PropList = P
.GetType()
.GetProperty("Properties")
.GetValue(P, null) as IEnumerable<PropertyInfo>; // notice "as ..."
// Since PropList is IEnumerable<T> you can loop over it
foreach (var thisProp in PropList) {
...
}
since GetValue(...) alone returns object.
After reviewing your code it's clear that
var PropList = P.GetType().GetProperty("Properties").GetValue(this, null)
does not return an ienumerable due to which you are getting and error. One possible solution To fix this issue you need to cast it to Ienumerable.
var PropList = P
.GetType()
.GetProperty("Properties")
.GetValue(this, null) as IEnumerable<PropertyInfo>;
I have the following loop over a dictionary type collection
foreach(KeyValuePair<Vector2, Object> entry in v_map.map_set)
{
}
I want to access the object properties, but the expected syntax doesn't work. E.G:
foreach(KeyValuePair<Vector2, Object> entry in v_map.map_set)
{
Object ob = entry.Value;
ob.property;
}
Fails because C# can't find the property wanted.
So, how do I access the desired properties?
solution:
foreach(KeyValuePair<Vector2, Object> entry in v_map.map_set)
{
if (entry.Value is warehouse)
{
warehouse ob = (warehouse)entry.Value;
}
}
If you know the type of the objects that are in the KeyValuePair, you can cast it to that type, and you will be able to find the properties you need.
And if you have several different objects stored, you can check which type it is by using is.
Like so:
if(entry.Value is Foo)
{
Foo lFoo = (Foo)entry.Value;
}
else if(entry.Value is Bar)
{
Bar lBar = (Bar)entry.Value;
}
You can make use of Refection to get the value of proerty of the object.
something like this
PropertyInfo info2 = object.GetType().GetProperty("prpertyname");
Object val = info2.GetValue(object, null);
You need to cast entry.Value to the type you need. The Object type itself isn't going to expose the properties you want.
If you just need to access the values, and you know the expected type you can use
foreach(ExpectedType value in v_map.map_set.Values.OfType<ExpectedType>())
{
var property = value.Property;
}
where Property is a property on ExpectedType.
The problem is that you're using an object which isn't typed. So you're going to need to use reflection like this:
PropertyInfo pi = ob.GetType().GetProperty("PropertyName");
var val = pi.GetValue(ob, null);
Now, if the property isn't public then you'll need to employ something else like this:
PropertyInfo pi = ob.GetType().GetProperty("PropertyName", BindingFlags.Instance | BindingFlags.NonPublic);
var val = pi.GetValue(ob, null);
Now, if this is actually a field you're trying to get to, you're going to need to do something different even yet:
FieldInfo fi = ob.GetType().GetField("fieldName");
var val = fi.GetValue(ob);
GetProperty method
BindingFlags enumeration
GetField method
I am trying to get the values from multiple properties in an entity framework object. There are 11 properties, each with a date assigned to it. I've tried using reflection but I keep getting an error " Object does not match target type"
public void CheckWeekStatus()
{
var currentFlexi = from c in FlexiContext.FlexPeriods where c.FlexiCurrentYear == true select c;
FlexPeriod s = new FlexPeriod();
PropertyInfo[] properties = s.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var info in properties)
{
var o = info.GetValue(currentFlexi,null);
}
}
FlexPeriod is the type that contains all the properties. I can loop through the properties but obviously I'm doing something wrong with the way I'm trying to access the values. Any suggestions would be appreciated.
Firstly, you can get the Type without instantiating an object:
PropertyInfo[] properties = typeof( FlexPeriod ).GetProperties( ...
The reason GetValue is failing is that currentFlexi is a collection of FlexPeriod objects ( actually an IEnumerable<FlexPeriod> ), not a single instance of FlexPeriod.
I am moving from VB to C#. I am trying to loop through a collection class which is a collection of data classes but I can't seem to get the actual values out of the data class properties(find the correct code to do so). I have a method that loops through the collection class(Contacts) and saves each record(Contact). I am using reflection because my method will not know if it is Contacts class or a Customer class and so forth. Here is my code in VB(watered down)
Public Function SaveCollection(ByVal objCollection as Object, ByVal TableName as string, ByVal spSave as string)
Dim objClass as Object
Dim propInfo as PropertyInfo
For Each objClass in objCollection
propInfo = objClass.GetType.GetProperty("TableFieldName")
Next
End Function
I am having problems in C# with the objClass.GetType.GetProperty("TableFieldName") line
Here is my C# code
public in SaveCollection(DictionaryBase objCollection, string TableName, string spSave)
{
PropertyInfo propInfo;
foreach (DictionaryEntry objClass in objCollection)
{
propInfo = objClass.GetType().GetProperty("TableFieldName")
}
}
The C# code keeps returning null. In my locals window I can see the proprties on the class on objClass and the value of the propery but I can seem to figure out how to access it through code. I used the DictionaryBase because that seems to closely match would I need to do. My data class(Contact) has a bunch or properties that match the field names in the database of the Contact Table. After I get the propInfo variable set I then set up my SQLParameter with the fieldname, datatype etc and then set the value to the propInfo.value.
Thanks for the help.
It looks like you are passing a different collection to the VB code and the C# code. My guess is that in the VB code you are passing the values of a dictionary and in C# you are passing the dictionary itself. Try changing the C# line to the following
propInfo = objClass.Value.GetType().GetProperty("TableFieldName");
I can't see why you're using a dictionary here - if it's just a collection, why not IEnumerable? Do you actually have a key here that you're storing things by?
DictionaryEntry will never have a TableFieldName property. Are you sure you don't need to take the value (or key) in the DictionaryEntry (using the Value or Key properties).
Do you really need to do this with reflection at all? Can't you use a generic method instead, after defining a common interface that has the TableFieldName property? For example:
public void SaveCollection<T>(IEnumerable<T> collection)
where T : ITableDescriptor
{
foreach (T element in collection)
{
string tableFieldName = element.TableFieldName;
// use the table field name here
}
}
you need to get the value afterwords. also note that GetValue returns an object, you can then cast it to string or int or whatever type the value you expect is in.
public in SaveCollection(DictionaryBase objCollection, string TableName, string spSave)
{
PropertyInfo propInfo;
foreach (DictionaryEntry objClass in objCollection)
{
object tempObj = objClass.Value;
propInfo = tempObj.GetType().GetProperty("TableFieldName");
object[] obRetVal = new Object[0];
object value = propInfo.GetValue(tempObj,obRetVal);
}
}
if you know that TableFieldName will be a string then change this line.
string value = propInfo.GetValue(tempObj,obRetVal) as string;
You're looking at the properties of the DictionaryEntry class. Not the class of the value in the particular key-value pair. Consider trying to use objClass.Value.GetType().GetProperty("TableFieldName").
Also consider switching to generics if you can. I think you should be able to do something like this:
VB:
Private l As List(Of MyClassName)
C#:
private List<MyClassName> l;
You may not want to use a list, but it's just an example. After that, accessing members of each item in the list can be done through intellisense. Often times generics can be faster too since you don't have to do any casting.