14

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

Cédric Boivin
  • 10,854
  • 13
  • 57
  • 98
  • Does it work when you set an integer instead of a string? `"1256"` is a string, not an integer. – Ken Browning Sep 28 '09 at 18:51
  • It will work, but the point is i dont know the type of the nullable property. I could use Nullable.GetUnderlyingType(property.PropertyType) to get the type – Cédric Boivin Sep 28 '09 at 18:57
  • `Nullable<>` cannot use `string` for the underlying type as `string` is a reference type. In other words `typeof(Nullable<>).MakeGenericType(t);` will be fine if `t == typeof(int)`, but it will explode (constraint not met) with `t == typeof(string)`. So `Nullable<>` will not in any way act as a kind of "common type" for reference types and nullable value types. – Jeppe Stig Nielsen Oct 04 '17 at 20:27

6 Answers6

18

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.

Ben M
  • 22,262
  • 3
  • 67
  • 71
9

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 );
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • I know it's a string but it's generic so i dont know what will be the type. It's only an example, my genric Nullable<> could be a string, int a generic conversion will be done – Cédric Boivin Sep 28 '09 at 18:53
  • 2
    You can't have a `Nullable`, since `string` is not a value type. – Ben M Sep 28 '09 at 18:55
5

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<>.

Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
3

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);
        }
    }
}
AdamCrawford
  • 4,968
  • 1
  • 18
  • 12
2

"1256" is a string, not an int.

Steve Gilham
  • 11,237
  • 3
  • 31
  • 37
1
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);
}
czlatea
  • 1,131
  • 13
  • 20