Consider the following sample code:
class SampleClass
{
public long SomeProperty { get; set; }
}
public void SetValue(SampleClass instance, decimal value)
{
// value is of type decimal, but is in reality a natural number => cast
instance.SomeProperty = (long)value;
}
Now I need to do something similar through reflection:
void SetValue(PropertyInfo info, object instance, object value)
{
// throws System.ArgumentException: Decimal can not be converted to Int64
info.SetValue(instance, value)
}
Note that I cannot assume that the PropertyInfo always represents a long, neither that value is always a decimal. However, I know that value can be casted to the correct type for that property.
How can I convert the 'value' parameter to the type represented by PropertyInfo instance through reflection ?
void SetValue(PropertyInfo info, object instance, object value)
{
info.SetValue(instance, Convert.ChangeType(value, info.PropertyType));
}
Thomas answer only works for types that implement IConvertible interface:
For the conversion to succeed, value must implement the IConvertible interface, because the method simply wraps a call to an appropriate IConvertible method. The method requires that conversion of value to conversionType be supported.
This code compile a linq expression that does the unboxing (if needed) and the conversion:
public static object Cast(this Type Type, object data)
{
var DataParam = Expression.Parameter(typeof(object), "data");
var Body = Expression.Block(Expression.Convert(Expression.Convert(DataParam, data.GetType()), Type));
var Run = Expression.Lambda(Body, DataParam).Compile();
var ret = Run.DynamicInvoke(data);
return ret;
}
The resulting lambda expression equals to (TOut)(TIn)Data where TIn is the type of the original data and TOut is the given type
The answer by Thomas is right, but I thought I would add my finding that Convert.ChangeType does not handle conversion to nullable types. To handle nullable types, I used the following code:
void SetValue(PropertyInfo info, object instance, object value)
{
var targetType = info.PropertyType.IsNullableType()
? Nullable.GetUnderlyingType(info.PropertyType)
: info.PropertyType;
var convertedValue = Convert.ChangeType(value, targetType);
info.SetValue(instance, convertedValue, null);
}
This code makes use of the following extension method:
public static class TypeExtensions
{
public static bool IsNullableType(this Type type)
{
return type.IsGenericType
&& type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
}
}
Contributing to jeroenh's answer, I would add that Convert.ChangeType crashes with a null value, so the line for getting the converted value should be:
var convertedValue = value == null ? null : Convert.ChangeType(value, targetType);
When the Type is a Nullable Guid then none of the above proposed solutions work.
Invalid cast from 'System.DBNull' to 'System.Guid' exception is thrown at Convert.ChangeType
To fix that change to:
var convertedValue = value == System.DBNull.Value ? null : Convert.ChangeType(value, targetType);
This is a very old question but I thought I'd chime in for ASP.NET Core Googlers.
In ASP.NET Core, .IsNullableType() is protected (amongst other changes) so the code is a tad different. Here's #jeroenh's answer modified to work in ASP.NET Core:
void SetValue(PropertyInfo info, object instance, object value)
{
Type proptype = info.PropertyType;
if (proptype.IsGenericType && proptype.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
proptype = new NullableConverter(info.PropertyType).UnderlyingType;
}
var convertedValue = Convert.ChangeType(value, proptype);
info.SetValue(instance, convertedValue);
}
Related
I'm trying to write an Extension method for nullable Enums.
Like with this example:
// ItemType is an enum
ItemType? item;
...
item.GetDescription();
So I wrote this method which doesn't compile for some reason that I don't understand:
public static string GetDescription(this Enum? theEnum)
{
if (theEnum == null)
return string.Empty;
return GetDescriptionAttribute(theEnum);
}
I'm getting the following error on Enum?:
only non-nullable value type could be underlying of system.nullable
Why? Enum can not have the value null!
Update:
If have lots of enums, ItemType is just an example of one of them.
System.Enum is a class, so just drop the ? and this should work.
(By "this should work", I mean if you pass in a null-valued ItemType?, you'll get a null Enum in the method.)
public static string GetDescription(this Enum theEnum)
{
if (theEnum == null)
return string.Empty;
return GetDescriptionAttribute(theEnum);
}
enum Test { blah }
Test? q = null;
q.GetDescription(); // => theEnum parameter is null
q = Test.blah;
q.GetDescription(); // => theEnum parameter is Test.blah
You can simply make your extension method generic:
public static string GetDescription<T>(this T? theEnum) where T : struct
{
if (!typeof(T).IsEnum)
throw new Exception("Must be an enum.");
if (theEnum == null)
return string.Empty;
return GetDescriptionAttribute(theEnum);
}
Unfortunately, you cannot use System.Enum in a generic constraint, so the extension method will show for all nullable values (hence the extra check).
EDIT: C# 7.3 introduced new generic constraints which now allow restricting a generic argument to an enum, like so:
public static string GetDescription<T>(this T? theEnum) where T : Enum
{
if (theEnum == null)
return string.Empty;
return GetDescriptionAttribute(theEnum);
}
Thanks #JeppeStigNielsen for pointing that out.
You should use the actual enum type in your method signature:
public static string GetDescription(this ItemType? theEnum)
System.ValueType and System.Enum are not treated as value types (only types derived from them), so they are nullable (and you don't to specify them as nullable). Try it:
// No errors!
ValueType v = null;
Enum e = null;
You could also try this signature:
public static string GetDescription<T>(this T? theEnum) where T: struct
This also allows structs though, which might not be what you want. I think i remember some library that adds a type constraint of enum after compilation (C# doesn't allow it) though. Just need to find it...
EDIT: Found it:
http://code.google.com/p/unconstrained-melody/
Maybe better is add extra value to your enum and call it null :)
I want to cast a string to a Type. Is use this code:
private static T CastToT<T>(string value)
{
return (T) Convert.ChangeType(value, typeof (T));
}
Type nr5 = CastToT<Type>(typeof(Class1).ToString());
But i got a InvalidCastException.
Invalid cast from 'System.String' to 'System.Type'.
Why can't I cast the string to a Type?
So if you want to retrieve a Type based on a name, you can simply use this method :
Type.GetType(typeName);
typeName is the fullname of the type you want to retrieve. This method will work if the type you are looking for is in the current assembly or in mscorlib.dll.
For example, based on your example :
private static Type CastToType(string value)
{
return Type.GetType(value);
}
And you call that method by giving the FullName of the type:
Type nr5 = CastToType(typeof(Random).FullName);
// nr5 will be null if no corresponding type is found.
if (nr5 != null && nr5.FullName == typeof(Random).FullName)
{
// success
}
Use can use this instead -
Assembly.GetType(string).
MSDN Link
If I have a generic type parameter that is a value type and I want to know if a value is equal to the default I test it like this:
static bool IsDefault<T>(T value){
where T: struct
return value.Equals(default(T));
}
If I don't have a generic type parameter, then it seems like I would have to use reflection. If the method has to work for all value types, then Is there a better way to perform this test than what I am doing here? :
static bool IsDefault(object value){
if(!(value is ValueType)){
throw new ArgumentException("Precondition failed: Must be a ValueType", "value");
}
var #default = Activator.CreateInstance(value.GetType());
return value.Equals(#default);
}
On a side note, Is there anything I am not considering here with respect to evaluating Nullable structs?
I have found the following extension methods useful and will work for all types:
public static object GetDefault(this Type t)
{
return t.IsValueType ? Activator.CreateInstance(t) : null;
}
public static T GetDefault<T>()
{
var t = typeof(T);
return (T) GetDefault(t);
}
public static bool IsDefault<T>(T other)
{
T defaultValue = GetDefault<T>();
if (other == null) return defaultValue == null;
return other.Equals(defaultValue);
}
Old question but the accepted answer doesn't work for me so I submit this (probably can be made better):
public static object GetDefault(this Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
var valueProperty = type.GetProperty("Value");
type = valueProperty.PropertyType;
}
return type.IsValueType ? Activator.CreateInstance(type) : null;
}
With the following results:
typeof(int).GetDefault(); // returns 0
typeof(int?).GetDefault(); // returns 0
typeof(DateTime).GetDefault(); // returns 01/01/0001 00:00:00
typeof(DateTime?).GetDefault(); // returns 01/01/0001 00:00:00
typeof(string).GetDefault(); // returns null
typeof(Exception).GetDefault(); // returns null
I would require ValueType as the parameter to simplify:
static bool IsDefault(ValueType value){
var #default = Activator.CreateInstance(value.GetType());
return value.Equals(#default);
}
On a side note, Is there anything I am not considering here with
respect to evaluating Nullable structs?
Yes, you are missing something. By taking an object as the parameter in you are requiring calling code to box Nullable<T> types (which converts them to null or to their T Value). So if you pass a nullable, your is/throw will throw because null will never be a value type.
Edit: As #cdhowie said, you'll need to check for null. This will work for Nullable types as well.
Imagine we have an enum:
enum Foo { A=1,B=2,C=3 }
If the type is known at compile-time, a direct cast can be used to change between the enum-type and the underlying type (usually int):
static int GetValue() { return 2; }
...
Foo foo = (Foo)GetValue(); // becomes Foo.B
And boxing this gives a box of type Foo:
object o1 = foo;
Console.WriteLine(o1.GetType().Name); // writes Foo
(and indeed, you can box as Foo and unbox as int, or box as int and unbox as Foo quite happily)
However (the problem); if the enum type is only known at runtime things are... trickier. It is obviously trivial to box it as an int - but can I box it as Foo? (Ideally without using generics and MakeGenericMethod, which would be ugly). Convert.ChangeType throws an exception. ToString and Enum.Parse works, but is horribly inefficient.
I could look at the defined values (Enum.GetValues or Type.GetFields), but that is very hard for [Flags], and even without would require getting back to the underlying-type first (which isn't as hard, thankfully).
But; is there a more direct to get from a value of the correct underlying-type to a box of the enum-type, where the type is only known at runtime?
I think the Enum.ToObject method will do what you want.
Type type= typeof(Foo);
object o1 = Enum.ToObject(type,GetValue());
Just wanted to add something to #aaronb's answer: I had to do this very thing for some auto-mapping code and found out that I needed to do several checks in order to make the code work for arbitrary types. In particular, null values and nullable enums will give you headaches.
The most foolproof code I have at the moment is this:
static object CastBoxedValue(object value, Type destType)
{
if (value == null)
return value;
Type enumType = GetEnumType(destType);
if (enumType != null)
return Enum.ToObject(enumType, value);
return value;
}
private static Type GetEnumType(Type type)
{
if (type.IsEnum)
return type;
if (type.IsGenericType)
{
var genericDef = type.GetGenericTypeDefinition();
if (genericDef == typeof(Nullable<>))
{
var genericArgs = type.GetGenericArguments();
return (genericArgs[0].IsEnum) ? genericArgs[0] : null;
}
}
return null;
}
If you can never have a nullable type then just ignore this. :)
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);
}