How to use MethodInfo.Invoke to set property value? - c#

I have a class with a property Value like this:
public class MyClass {
public property var Value { get; set; }
....
}
I want to use MethodInfo.Invoke() to set property value. Here are some codes:
object o;
// use CodeDom to get instance of a dynamically built MyClass to o, codes omitted
Type type = o.GetType();
MethodInfo mi = type.GetProperty("Value");
mi.Invoke(o, new object[] {23}); // Set Value to 23?
I cannot access to my work VS right now. My question is how to set Value with a integer value such as 23?

You can use the PropertyInfo.SetValue method.
object o;
//...
Type type = o.GetType();
PropertyInfo pi = type.GetProperty("Value");
pi.SetValue(o, 23, null);

If you are using .NET Framework 4.6 and 4.5, you can also use PropertyInfo.SetMethod Property :
object o;
//...
Type type = o.GetType();
PropertyInfo pi = type.GetProperty("Value");
pi.SetMethod.Invoke(o, new object[] {23});

Related

Using a property of T to create a new object of the property type with the property values?

I need a way to convert the properties of an object of type T to objects of the property type, and values as they are in T. My reason for doing this is so that I can check if the property is or inherits from IEnumerable (Lists, arrays, etc.), and if it does then I need to pass that IEnumerable as an object to be worked on. So far I have
foreach (var propInfo in obj.GetType().GetProperties())
{
var newObject = Activator.CreateInstance(propInfo.PropertyType, propInfo.GetValue(obj));
if (ObjectProcessing.ImplementsIEnumerable(newObject))
{
ObjectProcessing.ObjectQueue.Enqueue(newObject);
}
}
Which unfortunately doesn't work. I cant use CreatInstance<T> because it seems the compiler assumes T is the T in the method signature, which is the source object not the target object.
The question looks like a XY problem. What is the XY Problem?
You do not need to create an instance of the object to see if it implements or is IEnumerable. Let me build upon what you have so far
// This is the example object
public class MyClass {
public IEnumerable A{ get;set;}
public List<int> B{get;set;}
}
var myClass = new MyClass();
foreach (var propInfo in myClass.GetType().GetProperties()) {
var typeOfProperty = propInfo.PropertyType;
var isIEnuerableOrInheritingFromIt = typeof(IEnumerable).IsAssignableFrom(typeOfProperty);
if (isIEnuerableOrInheritingFromIt) {
var objectThatImplementsIEnumerable = propInfo.GetValue(myClass);
// Do stuff with it
}
}

Dynamically set INT value to Nullable enum property without explicit type cast

I have a method that populates a DataTable to simple DTO object. To simplify I'll use this example:
public enum Gender : int
{
Male = 1,
Female = 2
}
public class Person
{
//...
public Gender? MyGender { get; set; }
}
static void Main(string[] args)
{
int intValue = 2; // value from DB
var o = new Person();
var prop = o.GetType().GetProperty("MyGender");
prop.SetValue(o, intValue , null); // <- Exception
}
The above throws:
Object of type 'System.Int32' cannot be converted to type 'System.Nullable`1[Test.Program+Gender]'.
If I declare MyGender as Gender (not Nullable) everything works fine.
It also works if I use an explicit Cast prop.SetValue(o, (Gender)intValue, null);
BUT, I don't want to (and can't) use the explicit cast: (Gender)intValue because I have no knowledge of the underlying "hard" type when I create the DTO object .
I was hoping for something like (which dose not compile):
var propType = prop.PropertyType;
prop.SetValue(o, (propType)intValue, null);
I also tried:
public static dynamic Cast(dynamic obj, Type castTo)
{
return Convert.ChangeType(obj, castTo);
}
var propType = prop.PropertyType;
prop.SetValue(o, Cast(intValue, propType), null);
Which throws:
Invalid cast from 'System.Int32' to
'System.Nullable`1[[Test.Program+Gender...]
I am at dead end. what are my options?
.NET Framework 4.6.2
This is the best I can come up with. There's an explicit check to see whether the property being assigned to is nullable, but I don't think you can avoid that.
public static void Main(string[] args)
{
int intValue = 2; // value from DB
var o = new Person();
var prop = o.GetType().GetProperty("MyGender");
// Check whether the property is a nullable. If it is, get the type of underling enum
// Otherwise, get the type of the enum directly from the property
var enumType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
// Convert the int to the enum type
var convertedValue = Enum.ToObject(enumType, intValue);
prop.SetValue(o, convertedValue , null);
}
Of course, bad things will happen if the property being assigned isn't an enum. var convertedValue = enumType.IsEnum ? Enum.ToObject(enumType, intValue); : intValue; would avoid that, if you needed it.
A "creative" option to consider is:
var o = new Person();
o.MyGender = 0;
o.MyGender += intValue;
This looks odd, but it does work since the constant 0 has a built-in implicit cast to enum (which other numbers do not).
So, you set it to 0, and then increment it to the actual number you are interested in. A key benefit here is you don't take the performance hit (and / or lack of type safety) of using reflection. You may want to add a comment to the code as to why you are doing it as well. ;)

How to dynamically set the value of a property of object instance using reflection?

Given a basic class definition:
using System.Reflection;
public class Car()
{
public int speed {get;set;}
public void setSpeed()
{
Type type = this.GetType();
PropertyInfo property = type.GetProperty(PropertyName );
property.SetValue(type, Convert.ToInt32(PropertyValue), null);
}
}
This code sample is simplified and not using dynamic type conversion, I just want a working sample to set that property on the instance.
Edit: PropertyName and PropertyValue in above code is also simplified.
Thanks in advance
The first argument you pass should be the instance holding the property you wish to set. If it's a static property pass null for the first argument. In your case change the code to:
public void setSpeed()
{
Type type = this.GetType();
PropertyInfo property = type.GetProperty(PropertyName );
property.SetValue(this, Convert.ToInt32(PropertyValue), null);
}
for a naïve type conversion you could do
var value = Convert.ChangeType(PropertyValue,property.PropertyType);
property.SetValue(this, value, null);

I need to get a value of specific object's property, but don't know the type of the object

I have got an c# object and I don't know the type of this object. (i.e. object o)
What I know is that this object has a member called 'ID' of type int.
I wanted to get the value of this property but I am not good enough with reflection...
I can get a type and members of this object:
Type type = obj.GetType();
System.Reflection.MemberInfo[] member = type.GetMember("ID");
...but don't know what to do next :-)
Thanks for help in advance
Mariusz
Is this a public property? Is so then the easiest route is to use dynamic
int value = ((dynamic)obj).ID;
You can use:
Type type = obj.GetType();
PropertyInfo property = type.GetProperty("ID");
int id = (int) property.GetValue(obj, null);
Use PropertyInfo because you know it's a property, which makes things easier
Call GetValue to get the value, passing in obj as the target of the property and null for indexer arguments (as it's a property, not an index)
Cast the result to int as you already know it's going to be an int
Jared's suggestion of using dynamic is good too, if you're using C# 4 and .NET 4, although to avoid all the brackets I'd probably write it as:
dynamic d = obj;
int id = d.ID;
(unless you needed it in a single expression for some reason).
Can you use C# 4 ? In that case, you can use dynamic:
dynamic dyn = obj;
int id = dyn.ID;
public class TestClass
{
public TestClass()
{
// defaults
this.IdField = 1;
this.IdProperty = 2;
}
public int IdField;
public int IdProperty { get; set; }
}
// here is an object obj and you don't know which its underlying type
object obj = new TestClass();
var idProperty = obj.GetType().GetProperty("IdProperty");
if (idProperty != null)
{
// retrieve it and then parse to int using int.TryParse()
var intValue = idProperty.GetValue(obj, null);
}
var idField = obj.GetType().GetField("IdField");
if (idField != null)
{
// retrieve it and then parse to int using int.TryParse()
var intValue = idField.GetValue(obj);
}

Set property Nullable<> by reflection

I try to set a Nullable<> property dynamicly.
I Get my property ex :
PropertyInfo property = class.GetProperty("PropertyName"); // My property is Nullable<> at this time So the type could be a string or int
I want to set my property by reflection like
property.SetValue(class,"1256",null);
It's not working when my property is a Nullable<> Generic. So i try to find a way to set my property.
To know the type of my nullable<> property i execute
Nullable.GetUnderlyingType(property.PropertyType)
Any idea ?
I Try to create an instance of my Nullable<> property with
var nullVar = Activator.CreateInstance(typeof(Nullable<>).MakeGenericType(new Type[] { Nullable.GetUnderlyingType(property.PropertyType) }));
But nullVar is always Null
If you want to convert an arbitrary string to the underlying type of the Nullable, you can use the Convert class:
var propertyInfo = typeof(Foo).GetProperty("Bar");
object convertedValue = null;
try
{
convertedValue = System.Convert.ChangeType("1256",
Nullable.GetUnderlyingType(propertyInfo.PropertyType));
}
catch (InvalidCastException)
{
// the input string could not be converted to the target type - abort
return;
}
propertyInfo.SetValue(fooInstance, convertedValue, null);
This example will work if the target type is int, short, long (or unsigned variants, since the input string represents a non-negative number), double, float, or decimal. Caveat: this is not fast code.
If it's a nullable int, you'll need to use an int parameter, not a string.
property.SetValue(klass,1256,null);
Note the change to klass, instead of class, as class is a reserved keyword. You could also use #class if absolutely necessary (quoting it).
If your property is a generic, then I think you'll probably need to use Convert to convert whatever you have to whatever you need.
var nullType = Nullable.GetUnderlyingType(property.PropertyType)
var value = Convert.ChangeType("1256", nullType );
property.SetValue(klass, value, null );
Here is a complete example showing how to do it:
using System;
using System.Reflection;
class Test
{
static void Main()
{
Foo foo = new Foo();
typeof(Foo).GetProperty("Bar")
.SetValue(foo, 1234, null);
}
}
class Foo
{
public Nullable<Int32> Bar { get; set; }
}
As others have mentioned you need to pass the right type to the SetValue function but your other reflection code is not quite right either. You need to get the type of the class in question before you can query for its members.
Edit: If I understand correctly you are trying to set a string value to any property via reflection. In order to do this you will need to do some type inspection and type conversion.
Here is an example of what I mean:
using System;
using System.Reflection;
class Test
{
static void Main()
{
Foo foo = new Foo();
PropertyInfo property = typeof(Foo).GetProperty("Bar");
Object value =
Convert.ChangeType("1234",
Nullable.GetUnderlyingType(property.PropertyType)
?? property.PropertyType);
property.SetValue(foo, value, null);
}
}
class Foo
{
public Nullable<Int32> Bar { get; set; }
}
This approach can be safely used regardless of whether or not the property is Nullable<>.
I hit this same problem as well as an issue with Convert.ChangeType not handling DateTimes on Nullables so I combined a couple of stackoverflow solutions with some .NET 4 dynamic magic to get something I think is kind of sweet. If you look at the code, we use dynamic to type the object to Nullable at run time, then the run time treats it differently and allows assignments of the base type to the nullable object.
public void GenericMapField(object targetObj, string fieldName, object fieldValue)
{
PropertyInfo prop = targetObj.GetType().GetProperty(fieldName);
if (prop != null)
{
if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
dynamic objValue = System.Activator.CreateInstance(prop.PropertyType);
objValue = fieldValue;
prop.SetValue(targetObj, (object)objValue, null);
}
else
{
prop.SetValue(targetObj, fieldValue, null);
}
}
}
"1256" is a string, not an int.
public static void SetValue(object target, string propertyName, object value)
{
if (target == null)
return;
PropertyInfo propertyInfo = target.GetType().GetProperty(propertyName);
object convertedValue = value;
if (value != null && value.GetType() != propertyInfo.PropertyType)
{
Type propertyType = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
convertedValue = Convert.ChangeType(value, propertyType);
}
propertyInfo.SetValue(target, convertedValue, null);
}

Categories