Convert.ChangeType()在可空类型上失败

本文翻译自:Convert.ChangeType() fails on Nullable Types

I want to convert a string to an object property value, whose name I have as a string. 我想将字符串转换为对象属性值,我将其名称作为字符串。 I am trying to do this like so: 我正在尝试这样做:

string modelProperty = "Some Property Name";
string value = "SomeValue";
var property = entity.GetType().GetProperty(modelProperty);
if (property != null) {
    property.SetValue(entity, 
        Convert.ChangeType(value, property.PropertyType), null);
}

The problem is this is failing and throwing an Invalid Cast Exception when the property type is a nullable type. 问题是这失败了,并且当属性类型是可为空的类型时,抛出了无效的强制转换异常。 This is not the case of the values being unable to be Converted - they will work if I do this manually (eg DateTime? d = Convert.ToDateTime(value); ) I've seen some similiar questions but still can't get it to work. 这不是无法转换的值的情况-如果我手动执行此操作,它们将起作用(例如DateTime? d = Convert.ToDateTime(value); )我已经看到了一些类似的问题,但仍然无法获取去工作。


#1楼

参考:https://stackoom.com/question/eOEK/Convert-ChangeType-在可空类型上失败


#2楼

This is a little bit long-ish for an example, but this is a relatively robust approach, and separates the task of casting from unknown value to unknown type 举例来说,这有点长,但这是一个相对可靠的方法,将转换任务从未知值分离为未知类型

I have a TryCast method that does something similar, and takes nullable types into account. 我有一个类似的TryCast方法,并且将可空类型考虑在内。

public static bool TryCast<T>(this object value, out T result)
{
    var type = typeof (T);

    // If the type is nullable and the result should be null, set a null value.
    if (type.IsNullable() && (value == null || value == DBNull.Value))
    {
        result = default(T);
        return true;
    }

    // Convert.ChangeType fails on Nullable<T> types.  We want to try to cast to the underlying type anyway.
    var underlyingType = Nullable.GetUnderlyingType(type) ?? type;

    try
    {
        // Just one edge case you might want to handle.
        if (underlyingType == typeof(Guid))
        {
            if (value is string)
            {
                value = new Guid(value as string);
            }
            if (value is byte[])
            {
                value = new Guid(value as byte[]);
            }

            result = (T)Convert.ChangeType(value, underlyingType);
            return true;
        }

        result = (T)Convert.ChangeType(value, underlyingType);
        return true;
    }
    catch (Exception ex)
    {
        result = default(T);
        return false;
    }
}

Of course TryCast is a Method with a Type Parameter, so to call it dynamically you have to construct the MethodInfo yourself: 当然,TryCast是具有类型参数的方法,因此要动态调用它,您必须自己构造MethodInfo:

var constructedMethod = typeof (ObjectExtensions)
    .GetMethod("TryCast")
    .MakeGenericMethod(property.PropertyType);

Then to set the actual property value: 然后设置实际属性值:

public static void SetCastedValue<T>(this PropertyInfo property, T instance, object value)
{
    if (property.DeclaringType != typeof(T))
    {
        throw new ArgumentException("property's declaring type must be equal to typeof(T).");
    }

    var constructedMethod = typeof (ObjectExtensions)
        .GetMethod("TryCast")
        .MakeGenericMethod(property.PropertyType);

    object valueToSet = null;
    var parameters = new[] {value, null};
    var tryCastSucceeded = Convert.ToBoolean(constructedMethod.Invoke(null, parameters));
    if (tryCastSucceeded)
    {
        valueToSet = parameters[1];
    }

    if (!property.CanAssignValue(valueToSet))
    {
        return;
    }
    property.SetValue(instance, valueToSet, null);
}

And the extension methods to deal with property.CanAssignValue... 以及处理property.CanAssignValue ...的扩展方法

public static bool CanAssignValue(this PropertyInfo p, object value)
{
    return value == null ? p.IsNullable() : p.PropertyType.IsInstanceOfType(value);
}

public static bool IsNullable(this PropertyInfo p)
{
    return p.PropertyType.IsNullable();
}

public static bool IsNullable(this Type t)
{
    return !t.IsValueType || Nullable.GetUnderlyingType(t) != null;
}

#3楼

Thanks @LukeH 谢谢@LukeH
I changed a little: 我改变了一点:

public static object convertToPropType(PropertyInfo property, object value)
{
    object cstVal = null;
    if (property != null)
    {
        Type propType = Nullable.GetUnderlyingType(property.PropertyType);
        bool isNullable = (propType != null);
        if (!isNullable) { propType = property.PropertyType; }
        bool canAttrib = (value != null || isNullable);
        if (!canAttrib) { throw new Exception("Cant attrib null on non nullable. "); }
        cstVal = (value == null || Convert.IsDBNull(value)) ? null : Convert.ChangeType(value, propType);
    }
    return cstVal;
}

#4楼

I had a similar need, and the answer from LukeH pointed me in the direction. 我也有类似的需求,卢克(LukeH)的回答向我指明了方向。 I came up with this generic function to make it easy. 我想出了这个通用函数来简化它。

    public static Tout CopyValue<Tin, Tout>(Tin from, Tout toPrototype)
    {
        Type underlyingT = Nullable.GetUnderlyingType(typeof(Tout));
        if (underlyingT == null)
        { return (Tout)Convert.ChangeType(from, typeof(Tout)); }
        else
        { return (Tout)Convert.ChangeType(from, underlyingT); }
    }

Usage is like this: 用法是这样的:

        NotNullableDateProperty = CopyValue(NullableDateProperty, NotNullableDateProperty);

Note the second parameter is just used as a prototype to show the function how to cast the return value, so it doesn't actually have to be the destination property. 请注意,第二个参数仅用作原型,以显示函数如何转换返回值,因此实际上不必将其作为destination属性。 Meaning you can do also do something like this: 意味着您还可以执行以下操作:

        DateTime? source = new DateTime(2015, 1, 1);
        var dest = CopyValue(source, (string)null);

I did it this way instead of using an out because you can't use out with properties. 我这样做是为了代替使用out,因为您不能对属性使用out。 As is, it can work with properties and variables. 照原样,它可以使用属性和变量。 You could also create an overload to pass the type instead if you wanted. 如果需要,也可以创建一个重载来传递类型。


#5楼

You have to get the underlying type in order to do that... 为此,您必须获取基础类型...

Try this, I've used it successfully with generics: 尝试一下,我已经成功将其与泛型一起使用:

//Coalesce to get actual property type...
Type t = property.PropertyType();
t = Nullable.GetUnderlyingType(t) ?? t;

//Coalesce to set the safe value using default(t) or the safe type.
safeValue = value == null ? default(t) : Convert.ChangeType(value, t);

I use it in a number of places in my code, one example is a helper method I use for converting database values in a typesafe manner: 我在代码中的许多地方都使用了它,一个示例是一种用于以类型安全的方式转换数据库值的辅助方法:

public static T GetValue<T>(this IDataReader dr, string fieldName)
{
    object value = dr[fieldName];

    Type t = typeof(T);
    t = Nullable.GetUnderlyingType(t) ?? t;

    return (value == null || DBNull.Value.Equals(value)) ? 
        default(T) : (T)Convert.ChangeType(value, t);
}

Called using: 调用使用:

string field1 = dr.GetValue<string>("field1");
int? field2 = dr.GetValue<int?>("field2");
DateTime field3 = dr.GetValue<DateTime>("field3");

I wrote a series of blog posts including this at http://www.endswithsaurus.com/2010_07_01_archive.html (Scroll down to the Addendum, @JohnMacintyre actually spotted the bug in my original code which led me down the same path you're on now). 我在http://www.endswithsaurus.com/2010_07_01_archive.html上写了一系列博客文章(向下滚动至附录, @ JohnMacintyre实际上在我的原始代码中发现了该错误,从而使我沿着与您相同的路径前进。现在)。 I have a couple of small modifications since that post that includes conversion of enum types also so if your property is an Enum you can still use the same method call. 自从该帖子还包含枚举类型的转换以来,我进行了一些小修改,因此,如果您的属性是枚举,则仍然可以使用相同的方法调用。 Just add a line in to check for enum types and you're off to the races using something like: 只需添加一行以检查枚举类型,您就可以使用类似以下内容的竞赛了:

if (t.IsEnum)
    return (T)Enum.Parse(t, value);

Normally you'd have some error checking or use TryParse instead of Parse, but you get the picture. 通常,您会进行一些错误检查,或者使用TryParse代替Parse,但是您会得到图片。


#6楼

Untested, but maybe something like this will work: 未经测试,但也许这样可以工作:

string modelProperty = "Some Property Name";
string value = "Some Value";

var property = entity.GetType().GetProperty(modelProperty);
if (property != null)
{
    Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;

    object safeValue = (value == null) ? null : Convert.ChangeType(value, t);

    property.SetValue(entity, safeValue, null);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值