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>;
Related
I want to call the generic method, 'get_', for each property, IEnumerable<class>, of my view model class to avoid creating lengthy switch statements that explicitly get each list... Does anyone know how to get the object type and method generically?
foreach (var prop in vm.GetType().GetProperties().Where(x => x.GetCustomAttributes<ExportAttribute>().Any()))
{
var objType = ??;
var method = objType.GetMethod(<by name>);
var list = method.Invoke(prop, null);
foreach (var item in list)
{
//do something
}
}
I would use something like:
foreach (var prop in vm.GetType()
.GetProperties()
.Where(x => x.GetCustomAttributes<ExportAttribute>().Any()))
{
var list = (IEnumerable) prop.GetValue(vm, null);
foreach (var item in list)
{
// do something
}
}
Now item will be typed as object... but you can't really do better than that anyway. Your "do something" code can't use any members of the element type anyway, because it could be any type.
If, on the other hand, you know that every property will be something implementing IEnumerable<T> where T will in each case have a common base type, then due to the generic covariance of IEnumerable<T> introduced in .NET 4, you can write:
foreach (var prop in vm.GetType()
.GetProperties()
.Where(x => x.GetCustomAttributes<ExportAttribute>().Any()))
{
var list = (IEnumerable<BaseType>) prop.GetValue(vm, null);
foreach (var item in list)
{
// do something
}
}
Then you can access item.PropertyDeclaredInBaseType.
Note that I've changed the target of the call to vm instead of null, as presumably you want those instance properties...
this might be a simple fix but I can't seem to find anything about it. I am very new to C# if it's not obvious.
I'm passing a list of objects from my main method but I haven't been able to use the properties of the objects. I want to use a property called "Asset" This is my code:
private void GetDueDates(List<object> objects)
{
Type myType = objects.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
if(props.Contains(Asset)
{
doStuff();
}
}
I thought if I got the type of object then I could use the correct property but that didn't work. Do I even need to find which type it is?
Asset isn't a valid expression here, unless you've actually got a variable called Asset somewhere. You want to find out if the name of any property is Asset... and you want to do it on each object, not on the list itself:
foreach (var item in objects)
{
var props = item.GetType().GetProperties();
var assetProperty = props.FirstOrDefault(p => p.Name == "Asset");
if (assetProperty != null)
{
var value = assetProperty.GetValue(item, null);
// Do stuff...
}
}
Alternatively, if you're only looking for a public property, you can pass the name to GetProperty:
foreach (var item in objects)
{
var assetProperty = item.GetType().GetProperty("Asset");
if (assetProperty != null)
{
var value = assetProperty.GetValue(item, null);
// Do stuff...
}
}
Having said this, it would be cleaner if you had an interface or something similar:
var assetItems = objects.OfType<IHasAsset>();
foreach (var assetItem in assetItems)
{
var asset = assetItem.Asset;
...
}
If you know what type all of the objects in objects should be, then objects should be a list of that type, instead of a list of object (so List<MyType>). At this point, you can simply refer to objects[0].Asset, since all of the objects in the list are of type MyType.
If you still want objects to be a List<object>, then you'll have to typecast each of the objects in the list to a MyType in order to use the Asset property of the MyType class: ((MyType)objects[0]).Asset.
If you do this, and you aren't sure that all of the objects in the list are actually of type MyType, you need to check that:
if (objects[0] is MyType)
{
// do stuff with ((MyType)objects[0]).Asset
}
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.
I know I can create an object whose type is known only at run time like this:
Type t = record.GetType();
var src = Activator.CreateInstance(t.BaseType);
How can I do something like List<Record>=new List<Record>() at run time?
Suppose I am getting Child Record list using Reflection like this
var ChildRecorList=src.GetType().GetProperty(propName).GetValue(src, null);
and how can then I loop through this using foreach or for loop because foreach only works for known type list. It does now work with var types. Is there way to cast Reflection value to cast at specific type whose value is known at runtime(mentioned in point 1)
You can try this to create generic type in runtime:
Type genericListType = typeof (List<>);
// if you have more than one generic argumens
// you can add your types here like typeof(MyClass),typeof(MyClass2)
Type[] genericArguments = { typeof (Record) };
// create your generic type with generic arguments
Type myGenericType = genericListType.MakeGenericType(genericArguments);
// and then you can create your instance
var recordList = Activator.CreateInstance(myGenericType);
// get your property value
recordList = src.GetType().GetProperty(propName).GetValue(src, null);
And I guess you sure your type is a List then when you creating your instance you can make a cast like this:
var recordList = (IList)Activator.CreateInstance(myGenericType);
Then you can loop through your list
foreach (var item in recordList)
{
...
}
I'm not sure If you're looking for something like this, But you can use List Inside another List or Dictionary, Also, You can store the base value with any type and get the type whenever needed, I recommend using Dictionary so that way you can name your lists:
Dictionary<String, List<Object>> Data = new Dictionary<String, List<Object>>();
Data["MyUser"] = new List<Object>();
Data["MyUser"].Add(MyDBObject);
var obj = Data["MyUser"].Find(x => x.MyKey == "MyKeyObject");
var t = obj.GetType();
var dta = (MyDBObject)obj;
foreach (var db in Data)
{
if (db.Key == "MyUser")
db.Value.Find(x => x.Name == "MyName");
}
Or If you're looking for Creating lists on runtime:
List<List<Object>> Data = new List<List<Object>>();
var mydb = Data.Count;
Data.Add(new List<Object>());
Data[mydb].Add(MyOBJ);
foreach (var db in Data)
{
if (db.Contains(MyOBJ)
return db;
}
Why don't you just use generics? You could do something like this:
public void DoSomething<T>(T type)
{
var list = new List<T>();
}
This will allow you to create a list from whichever type is passed in at runtime.
I have two object of same class, I want to update the p2 with fields which are are in Dirty list. So far I managed to write the following code but struggling to get the value of p1 properties. What object should I pass here as parameter to GetValue method.
Person p1 = new Person();
p1.FirstName = "Test";
Person p2 = new Person();
var allDirtyFields = p1.GetAllDirtyFields();
foreach (var dirtyField in allDirtyFields)
{
p2.GetType()
.GetProperty(dirtyField)
.SetValue(p1.GetType().GetProperty(dirtyField).GetValue());
}
_context.UpdateObject(p2);
_context.SaveChanges();
Thanks in advance.
You should try that:
foreach (var dirtyField in allDirtyFields)
{
var prop = p2.GetType().GetProperty(dirtyField);
prop.SetValue(p2, prop.GetValue(p1));
}
It is a better to store PropertyInfo instance in a variable, then trying to resolve it twice.
Did you know that you don't need to retrieve the property for each object?
Type metadata is common to any object of the whole type.
For example:
// Firstly, get dirty property informations!
IEnumerable<PropertyInfo> dirtyProperties = p2.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where
(
property => allDirtyFields.Any
(
field => property.Name == field
)
);
// Then, just iterate the whole property informations, but give the
// "obj" GetValue/SetValue first argument the references "p2" or "p1" as follows:
foreach(PropertyInfo dirtyProperty in dirtyProperties)
{
dirtyProperty.SetValue(p2, dirtyProperty.GetValue(p1));
}
Check that the first parameter of PropertyInfo.GetValue(...) and PropertyInfo.SetValue(...) is the object for which you want to get or set the value of the whole property.
In each iteration, you have to get a reference to the PropertyInfo. When you call it's SetValue method, you should pass in 2 parameters, the object for which you will set the property and the actual value you are setting. For the latter one, you should invoke the GetValue method on the same property, passing in the p1 object as parameter, i.e. the source for the value.
Try this:
foreach (var dirtyField in allDirtyFields)
{
var p = p2.GetType().GetProperty(dirtyField);
p.SetValue(p2, p.GetValue(p1));
}
I would recommend you to keep the dirtyField variables in a dictionary and retrieve the associated PropertyInfo object from this dictionary. It should be much faster.
Firstly, declare some static variable in your class:
static Dictionary<string, PropertyInfo>
personProps = new Dictionary<string, PropertyInfo>();
Then you may change your method to:
foreach (var dirtyField in allDirtyFields)
{
PropertyInfo p = null;
if (!personProps.ContainsKey(dirtyField))
{
p = p2.GetType().GetProperty(dirtyField);
personProps.Add(dirtyField, p);
}
else
{
p = personProps[dirtyField];
}
p.SetValue(p2, p.GetValue(p1));
}
You need to pass the instance from which you want to get the property value, like so:
p1.GetType().GetProperty(dirtyField).GetValue(p1, null)
The second parameter, can be used to retrieve a value at a certain index if the property type is indexed.
IIrc you send p1 being the instance that holds the value and null to indicate you're not searching for a specific index value.