Get number of enum values from instance - c#

I'm trying to loop through all of the enums in an object and for each enum, I need to get the number of values for its particular type. So far I have the following code:
var enumProps = testObj.GetType().GetProperties().Where(p => p.PropertyType.IsEnum);
foreach (var prop in enumProps)
{
var propType = prop.GetType();
var valueCount = Enum.GetValues(typeof(propType)).Length; // <-- error
}
The problem is that when I pass propType to typeof, I get the following error:
propType is a variable but used as a type.
Anyone see where I'm going wrong here?

GetType produces the type of prop, which is fixed (i.e. PropertyInfo reflection class). You need to use PropertyType member, like this:
foreach (var prop in enumProps) {
var propType = prop.PropertyType;
var valueCount = Enum.GetValues(propType).Length;
}
Demo.
Note that typeof operator is not necessary, because propType is already a System.Type that you need.

Related

Better solution to copy IEnumerable<T> to IList<T> using reflection

I have provided a dotnetfiddle to show the issue.
I try to copy object from a source that have the same property names and type except some properties that have IEnumerable and target object has
IList using reflection.
public T CopyTo<T>(object src)
where T : new()
{
var targetObj = new T();
//Getting Type of Src
var sourceType = src.GetType();
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty;
var sourcePi = sourceType.GetProperties(flags);
foreach (var property in sourcePi)
{
var pi = targetObj.GetType().GetProperty(property.Name);
if (pi == null || !pi.CanWrite)
continue;
object sourceValue = property.GetValue(src, null);
//var sourceValue = Convert.ChangeType(property.GetValue(src, null), pi.PropertyType);
//this works, but hard wired
if (sourceValue is IEnumerable<string> i)
sourceValue = ((IEnumerable<string>)i).Cast<string>().ToList();
pi.SetValue(targetObj, sourceValue, null);
}
return targetObj;
}
It raises an error:
System.ArgumentException: 'Object of type 'System.String[]' cannot be
converted to type 'System.Collections.Generic.List`1[System.String]'.'
I tried to convert:
var sourceValue = Convert.ChangeType(property.GetValue(src, null), pi.PropertyType);
but also get error
System.InvalidCastException: Object must implement IConvertible.
this issue can't help.
My workaround solution is casting :
sourceValue = ((IEnumerable<string>) sourceValue).Cast<string>().ToList();
The disadvantage is hard wiring the cast to IEnumerable<string>
Is there a better way to copy IEnumerable<T> to IList<T> or any generic collection using reflection.
A simple approach would be to check if IEnumerable<T> is assignable from the source property's type, check if the destination property's type is assignable from List<T>, and if so then assign a new List<T> to the destination property passing the source property's value into the constructor.
public T CopyTo<T>(object src) where T : new()
{
var targetObj = new T();
var sourceType = src.GetType();
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty;
var sourcePi = sourceType.GetProperties(flags);
foreach (var property in sourcePi)
{
var pi = targetObj.GetType().GetProperty(property.Name);
if (pi == null || !pi.CanWrite)
continue;
object sourceValue = property.GetValue(src, null);
//var sourceValue = Convert.ChangeType(property.GetValue(src, null), pi.PropertyType);
//this works, but hard wired
if (property.PropertyType.IsGenericType)
{
Type enumerable = typeof(IEnumerable<>).MakeGenericType(property.PropertyType.GenericTypeArguments);
if (enumerable.IsAssignableFrom(property.PropertyType))
{
Type list = typeof(List<>).MakeGenericType(property.PropertyType.GenericTypeArguments);
if (pi.PropertyType.IsAssignableFrom(list))
{
var pValue = Activator.CreateInstance(list, new[] { sourceValue });
pi.SetValue(targetObj, pValue, null);
}
}
}
else
{
pi.SetValue(targetObj, sourceValue, null);
}
}
return targetObj;
}
It isn't hard to imagine situations where this wouldn't work... for example if the destination type's property is LinkedList<T> or T[] then the properties won't copy. You'd have to modify it further to handle cases like this.
This would also create a new List<T> for the destination object even if the properties on the two objects are both exactly the same type. It isn't clear if that is what you want or not, but it is different from how other properties are copied by this method so it is worth mentioning.
I think you need to add a check of the destination type too.
And then, With the two different type (source and destination),
You can create a function that will handle the conversion/cast.
Something like this:
1. Source IEnumerable<T>, Destination: List<T> => Create New list and put in CTOR
2. Source List<T>, Destination: List<T> => Just Copy
3. Source List<T>, Destination: IEnumerable<T> => Create a list
4. Source IEnumerable<T> ,Destination: IEnumerable<T> => Made a copy?

Unable to cast object of type 'System.Reflection.RuntimePropertyInfo' to type 'System.IConvertible'

I am having issues with the following code. I am iterating through a set to see if an object has a certain property type and if so, do some conversion to it.
var props = obj.GetType().GetProperties();
foreach (var p in props)
{
if (p.PropertyType == typeof(System.Boolean))
{
var conv = Convert.ToByte(p);
}
}
I get the following error when I try to run it:
"Unable to cast object of type 'System.Reflection.RuntimePropertyInfo' to type 'System.IConvertible'."
I assume you want to get the value of the property and convert that to a byte, right? Not the property itself... so:
var conv = Convert.ToByte(p.GetValue(obj, null));
It seems odd to have it as a byte rather than a bool, admittedly... I'd have expected:
var conv = (bool) p.GetValue(obj, null);
I'd also personally use:
if (p.PropertyType == typeof(bool))
rather than spelling out the System.Boolean explicitly.

Looping though a dictionary containing objects

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

Get value of a specific object property in C# without knowing the class behind

I have an object (.NET) of type "object". I don't know the "real type (class)" behind it during runtime , but I know, that the object has a property "string name". How can I retrive the value of "name"? Is this possible?
something like this:
object item = AnyFunction(....);
string value = item.name;
Use reflection
System.Reflection.PropertyInfo pi = item.GetType().GetProperty("name");
String name = (String)(pi.GetValue(item, null));
You can do it using dynamic instead of object:
dynamic item = AnyFunction(....);
string value = item.name;
Note that the Dynamic Language Runtime (DLR) has built-in caching mechanisms, so subsequent calls are very fast.
Reflection can help you.
var someObject;
var propertyName = "PropertyWhichValueYouWantToKnow";
var propertyName = someObject.GetType().GetProperty(propertyName).GetValue(someObject, null);
Reflection and dynamic value access are correct solutions to this question but are quite slow.
If your want something faster then you can create dynamic method using expressions:
object value = GetValue();
string propertyName = "MyProperty";
var parameter = Expression.Parameter(typeof(object));
var cast = Expression.Convert(parameter, value.GetType());
var propertyGetter = Expression.Property(cast, propertyName);
var castResult = Expression.Convert(propertyGetter, typeof(object));//for boxing
var propertyRetriver = Expression.Lambda<Func<object, object>>(castResult, parameter).Compile();
var retrivedPropertyValue = propertyRetriver(value);
This way is faster if you cache created functions. For instance in dictionary where key would be the actual type of object assuming that property name is not changing or some combination of type and property name.
In some cases, Reflection doesn't work properly.
You could use dictionaries, if all item types are the same.
For instance, if your items are strings :
Dictionary<string, string> response = JsonConvert.DeserializeObject<Dictionary<string, string>>(item);
Or ints:
Dictionary<string, int> response = JsonConvert.DeserializeObject<Dictionary<string, int>>(item);
You can do it using dynamic instead of object:
dynamic item = AnyFunction(....);
string value = item["name"].Value;
Simply try this for all properties of an object,
foreach (var prop in myobject.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance))
{
var propertyName = prop.Name;
var propertyValue = myobject.GetType().GetProperty(propertyName).GetValue(myobject, null);
//Debug.Print(prop.Name);
//Debug.Print(Functions.convertNullableToString(propertyValue));
Debug.Print(string.Format("Property Name={0} , Value={1}", prop.Name, Functions.convertNullableToString(propertyValue)));
}
NOTE: Functions.convertNullableToString() is custom function using for convert NULL value into string.empty.

Dynamically access to a PropertyInfo whose Type is IEnumerable<customClass>

I got this code thats works :
var assembly= Assembly.LoadFrom("D:\\...\\mydll.dll");
Type rmType = assembly.GetType(specific_class_with_namespace);
bject MyObjj = Activator.CreateInstance(rmType);
PropertyInfo[] pi = rmType.GetProperties();
foreach(PropertyInfo prop in pi)
{
Console.WriteLine("Prop: {0}", prop.Name);
}
The thing is that the property I wanna access to is a IEnumerable<IWidget>, where IWidget is an internal class of my dll. So in my current code I can't test the Type of my PropertyInfo (and cast it right ? :/)
The final objective is to access to the Name property of my IWidget. Something like :
foreach(var widget in myProperty)
{
string widgetName = widget.Name;
}
I read a couple of tutorials on the web and some questions here on SO, but none helped me for my IEnumerable<customType> problem :/
EDIT :
when I do :
PropertyInfo ItemsProperty = rmType.GetProperty("Items");
var ItemsPropertyValue = ItemsProperty.GetValue(rmType, null);
I can a XamlParseException with the "most" InnerException being "Object does not match target type"...
You can test you property for being of type IEnumerable and then in foreach loop with reflection you can access property of item with name Name.
To test for being IEnumerable you can use typeof(IEnumerable).IsAssignableFrom(prop.PropertyType) or type.GetGenericTypeDefinition() == typeof(IEnumerable<>).
EDIT:
In GetValue you should put instance not the type:
var ItemsPropertyValue = ItemsProperty.GetValue(MyObjj, null);

Categories