使用反射设置值 - 使用转换时出错

Har*_*oon 1 c# reflection .net-3.5

我使用下面的代码来设置类的值,这个类的一些值是string decimal decimal? int?等等.

我有一个字段列表 - 其值为字符串,.net正在抛出以下异常:

System.InvalidCastException : Invalid cast from 'System.String' to 'System.Nullable`1[[System.Decimal, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'.
at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
at System.String.System.IConvertible.ToType(Type type, IFormatProvider provider)
at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
at System.Convert.ChangeType(Object value, Type conversionType)
at Surventrix.Domain.Model.Entities.StatisticalData.UpdateStatisticalData(ReportCommit commit, ILogProvider log) in StatisticalData.cs: line 591
at Surventrix.Tests.Stats.StatsTest.CreateStatsFromCommit() in StatsTest.cs: line 32 
Run Code Online (Sandbox Code Playgroud)

我的代码是:

    public void UpdateStatisticalData(ReportCommit commit, ILogProvider log)
    {
        var fields = commit.CurrentFieldList.ToList();

        var properties = typeof(StatisticalData).GetProperties();

        foreach (var p in properties)
        {
            log.LogMessage("what my name: {0}", p.Name);
            // If not writable then cannot null it; if not readable then cannot check it's value
            if (!p.CanWrite || !p.CanRead) { continue; }

            var mget = p.GetGetMethod(false);
            var mset = p.GetSetMethod(false);

            // Get and set methods have to be public
            if (mget == null) { continue; }
            if (mset == null) { continue; }


            var val = fields.SingleOrDefault(x => p.Name == x.Name);

            if (val == null) continue;

            //field.value is stored as a string
            if (string.IsNullOrEmpty(val.Value)) continue;

            log.LogMessage("set: {0} ----> {1}", p.Name, val.Value);

            var typedVal = Convert.ChangeType(val.Value, p.PropertyType);

            p.SetValue(this, typedVal, null);
        }

    }
Run Code Online (Sandbox Code Playgroud)

问题:如何修复我的代码以便不抛出此异常,我真的不明白为什么会抛出此异常...

更新 - 日志结果*

what my name: StatisticalDataID
what my name: OfficeDistanceFromProperty
what my name: OfficeAddress1
set: OfficeAddress1 ----> North Warwickshire House
what my name: OfficeAddress2
set: OfficeAddress2 ----> 92 Wheat Street
what my name: OfficeAddress3
what my name: OfficeCounty
what my name: OfficeTown
set: OfficeTown ----> Nuneaton
what my name: OfficePostcode
set: OfficePostcode ----> CV11 4BH
what my name: ResidentialInternalFloorArea
what my name: ValuationCalculationSqFtAssumed
what my name: SubjectPropertyAddress1
set: SubjectPropertyAddress1 ----> 323 Stanton road
what my name: SubjectPropertyAddress2
set: SubjectPropertyAddress2 ----> cbvcb
what my name: SubjectPropertyAddress3
set: SubjectPropertyAddress3 ----> vcbc
what my name: SubjectPropertyTown
set: SubjectPropertyTown ----> Coventry
what my name: SubjectPropertyCounty
set: SubjectPropertyCounty ----> bcbvc
what my name: SubjectPropertyPostCode
set: SubjectPropertyPostCode ----> CV1 4HH
what my name: OccupierName
set: OccupierName ----> Mr Peters
what my name: AdvanceAmount
set: AdvanceAmount ----> 0
Run Code Online (Sandbox Code Playgroud)

更新 - 我已更新到你的代码@Jon,我正在调用这样的方法:

            var typedVal = NullableSafeChangeType(val.Value, p.PropertyType);

            if (!string.IsNullOrEmpty(val.Value))
                p.SetValue(this, typedVal, null);
Run Code Online (Sandbox Code Playgroud)

这引发了一个错误:

            _log.LogMessage("error is here ---> {0}", input);
            return input == null || input == "" ? null : Convert.ChangeType(input, underlyingType);
Run Code Online (Sandbox Code Playgroud)

输入是任何有效的字符串,类型(System.String)

Jon*_*eet 5

它与设置属性无关,而与更改类型有关.这是一个简短而完整的示例,演示了这个问题:

using System;

class Test
{
    static void Main()
    {
        object converted = Convert.ChangeType("10", typeof(int?));
        Console.WriteLine(converted);
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上,Convert.ChangeType不支持Nullable<T>.你必须自己处理.您可以编写一个检测目标类型的方法Nullable<T>,并返回null(如果原始字符串值为null或对空字符串的引用)或将其转换为基础类型的结果.

编辑:例如(完全未经测试):

static object NullableSafeChangeType(string input, Type type)
{
    Type underlyingType = Nullable.GetUnderlyingType(type);
    if (underlyingType == null) // Non-nullable; convert directly
    {
        return Convert.ChangeType(input, type);
    }
    else
    {
        return input == null || input == "" ? null
            : Convert.ChangeType(input, underlyingType);
    }
}
Run Code Online (Sandbox Code Playgroud)