简体   繁体   中英

Setting the value using reflection - error with casting

I am using the code below to set the values of a class, some values on this class are string decimal decimal? int? etc.

I have a list of fields - with its value as a string, .net is throwing the exception below:

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 

my code is:

    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);
        }

    }

Question: how can I fix my code so this exception is not thrown, i dont really understand why this exception is being thrown here...

update - result of log *

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

Update - I have updated to your code @Jon, I am calling the method like so:

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

            if (!string.IsNullOrEmpty(val.Value))
                p.SetValue(this, typedVal, null);

which throws an error at:

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

The input is any valid string, type(System.String)

It's got nothing to do with setting the property, and everything to do with changing the type. Here's a short but complete example demonstrating the problem:

using System;

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

Basically, Convert.ChangeType doesn't support Nullable<T> . You'll have to handle that yourself. You could write a method which detect that the target type is Nullable<T> , and either returns null (if the original string value is null or a reference to an empty string) or the result of converting it to the underlying type.

EDIT: For example (completely untested):

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);
    }
}

I think you need the follow method:

    private static T Convert<T>(string input)
    {
        if (input == null) throw new ArgumentNullException("input");
        var converter = TypeDescriptor.GetConverter(typeof(T));
        if (input.Is(typeof(T)))
        {
            try
            {
                return (T)converter.ConvertFromString(input);
            }
            catch (NotSupportedException notSupportedException)
            {
                Console.WriteLine(notSupportedException);
            }
        }
        return default(T);
    }

Using:

                try
                {
                    // If Value is not a string => Convert over Generic Method
                    MethodInfo method = typeof(whereConvertImplemented).GetMethod("Convert", BindingFlags.NonPublic | BindingFlags.Static);
                    MethodInfo generic = method.MakeGenericMethod(property.PropertyType);
                    var value = generic.Invoke(this, new object[] { propvalue });
                    property.SetValue(this, value, null);
                }
                catch
                {

                }

Hope it works for you :)

Greetings

This line

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

will not work for nullable types. You have add a special case for property types which are nullable types, and if the value if null or empty, then set the property value to null.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM