When I foreach through an object's properties and end up with a PropertyInfo, how can I access the actual property of the object?
#foreach(var propertyInfo in Model.Entity.GetType().GetProperties()) {
if (propertyInfo.PropertyType != typeof(string) &&
propertyInfo.PropertyType.GetInterface(typeof(IEnumerable<>).Name) != null) {
<div>
#foreach(var item in propertyInfo/*Need Actual Property Here*/) {
#Html.Action("Edit", new { Id = item.Id, typeName = "Item" });
}
</div>;
}
}
Well, you need an instance to call it on - presumably that's Model.Entity in this case. You need the GetValue method. However, it's going to be quite tricky - because you need two things:
You need the value to be iterable by foreach
You need each item to have an Id property.
If you're using C# 4 and .NET 4, you can use dynamic typing to make it a bit simpler:
IEnumerable values = (IEnumerable) propertyInfo.GetValue(Model.Entity, null);
#foreach(dynamic item in values) {
#Html.Action("Edit", new { Id = item.Id, typeName = "Item" });
}
Or even:
dynamic values = propertyInfo.GetValue(Model.Entity, null);
#foreach(dynamic item in values) {
#Html.Action("Edit", new { Id = item.Id, typeName = "Item" });
}
When you say get the "actual property of the object" if you are referring to the value of the property then you can do something like below.
var item = in propertyInfo.GetValue(Model.Entity, null);
However if you don't have the proper type resolved (which should be object in the above example using var for inference) you will not be able to access the Id property of the value without further reflection or a different dynamic method for accessing the data.
Edit: modified the example not to be in the foreach as #Jon Skeet pointed out it wouldn't compile without the cast and this is moreover to demonstrate retrieving a value from PropertyInfo
Related
I have to cast enum but I want this to be as generic as possbile. How can I replace the Cast<XX>() part with propertyType?
foreach (var prop in MainType.GetProperties().Where(x => x.PropertyType.IsEnum))
{
var x = new { name = prop.Name, values = new List<object>() };
foreach (var v in Enum.GetValues(prop.PropertyType).Cast<XX>())
x.values.Add(new { p = v.GetAttribute<DescriptionAttribute>().Description,
c = v.GetAttribute<DefaultValueAttribute>().Value });
o.Add(x);
}
public static TAttribute GetAttribute<TAttribute>(this Enum value) where TAttribute : Attribute
{
var type = value.GetType();
var name = Enum.GetName(type, value);
return type.GetField(name)
.GetCustomAttributes(false)
.OfType<TAttribute>()
.SingleOrDefault();
}
public enum JobApplicationState : short
{
[Description("Active")]
[DefaultValue(typeof(string), "bg-primary text-highlight")]
Active = 1,
[Description("Passive")]
[DefaultValue(typeof(string), "bg-grey text-highlight")]
Passive = 2,
[Description("Rejected")]
[DefaultValue(typeof(string), "bg-danger text-highlight")]
Rejected = 3,
[Description("Accepted")]
[DefaultValue(typeof(string), "bg-success text-highlight")]
Accepted = 4
}
THIS WORKED!
foreach (MemberInfo m in prop.PropertyType.GetFields())
{
if (m.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault() != null && m.GetCustomAttributes(typeof(DefaultValueAttribute), true).FirstOrDefault() != null)
{
x.values.Add(
new
{
p = ((DescriptionAttribute)m.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault()).Description,
c = ((DefaultValueAttribute)m.GetCustomAttributes(typeof(DefaultValueAttribute), true).FirstOrDefault()).Value
});
}
}
You should realize that an enum is nothing more than a named wrapper around an integral value. I've described some details on how enums work here: Cast int to enum in C# .
The question here is to get attributes values from an enum. As you might imagine, this question is about getting attributes from the type, not about getting attributes from the value (there's no such thing). Still, if you call a method like void Foo<T>(T myEnum), the type T will hold all the necessary info, even though 'in real life' the value of myEnum is passed around as an integral type.
From this you can also deduce that you're actually looking for the attributes of the MemberInfo's of T, since you're looking for the members of the type (e.g. the enum). Again, all the details are in my post on how enums work. Hence the answer:
foreach (MemberInfo m in prop.PropertyType.GetFields())
{
// use m.GetAttribute(...)
}
Why donĀ“t you simply cast to Enum:
foreach (var v in Enum.GetValues(prop.PropertyType).Cast<Enum>())
Now you can call your extension-method on every element within the returned list.
v.GetAttribute<MyType>()
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 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.
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