Выражение для преобразования числовых значений: System.InvalidCastException:> Невозможно привести объект типа 'System.Int32' к типу 'System.Int64' - PullRequest
1 голос
/ 22 октября 2019

Я пытаюсь создать выражение для преобразования некоторых числовых значений. Вот что я попробовал:

public object ConvertValue(object value, Type targetType) {
    var parameter = Expression.Parameter(typeof(object), "p"); // "p"
    var convert = Expression.Convert(parameter, targetType); // Convert(p, Int64)
    var targetConvert = Expression.Convert(convert, typeof(object)); // Convert(Convert(p, Int64), Object)
    var lambda = Expression.Lambda<Func<object,object>>(targetConvert, parameter); // p => Convert(Convert(p, Int64), Object)
    var method = lambda.Compile();
    var result = method(value); // HERE I GET THE ERROR!
    return result;
}

Но когда я называю это, как этот простой тест:

[Fact]
public void TestConvert() {
    var result = ConvertValue(23, typeof(long));
    Assert.Equal(typeof(long), result.GetType());
}

Я получаю ошибку:

System.Reflection.TargetInvocationException: исключение было сгенерировано целью вызова. ---> System.InvalidCastException: Невозможно привести объект типа 'System.Int32' к типу 'System.Int64'.
в lambda_method (Closure, Object) бла-бла-бла-бла ...

Есть идеи, что здесь происходит и что Int32 нельзя привести к Int64? Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 23 октября 2019

То, что вы пытаетесь сделать, это (int64)(object)23, и оно не будет работать, потому что 23 имеет тип int32. Вы можете передать 23L литерал, и он будет работать:

ConvertValue(23L, typeof(long));

Чтобы ваш код работал, вам нужно вызвать конвертирование из int32 в int64 после распаковки:

public static object ConvertValue(object value, Type targetType) {
    var parameter = Expression.Parameter(typeof(object), "p"); // "p"
    var convert = Expression.Convert(Expression.Convert(parameter, value.GetType()), targetType); // Convert(p, Int64)
    var targetConvert = Expression.Convert(convert, typeof(object)); // Convert(Convert(p, Int64), Object)
    var lambda = Expression.Lambda<Func<object,object>>(targetConvert, parameter); // p => Convert(Convert(p, Int64), Object)
    var method = lambda.Compile();
    var result = method(value);
    return result;
}
0 голосов
/ 23 октября 2019

Следующее использует Convert.ChangeType для обеспечения большей гибкости с преобразованиями.

Код комментирует документ о том, что делается.

public object ConvertValue(object value, Type targetType){
    var valueType = value.GetType();
    // Func<TValue,TTarget>
    var delegateType = typeof(Func<,>).MakeGenericType(valueType, targetType);
    var convert = typeof(Convert).GetMethod("ChangeType", new[] { typeof(object), typeof(Type) });
    // TValue p
    var parameter = Expression.Parameter(valueType, "p");
    // Convert.ChangeType(Convert(p), targetType);
    var changeType = Expression.Call(convert, Expression.Convert(parameter, typeof(object)), Expression.Constant(targetType));
    // (TTarget)Convert.ChangeType(Convert(p), targetType);
    var body = Expression.Convert(changeType, targetType);
    //Func<TValue,TTarget> = TValue p => (TTarget)Convert.ChangeType(Convert(p), targetType);
    var lambda = Expression.Lambda(delegateType, body, parameter);
    var method = lambda.Compile();
    var result = method.DynamicInvoke(value);
    return result;
}

Следующие базовые тесты все прошли, когдатренируется

[TestMethod]
public void Should_Convert_Int_To_Long() {
    var expected = typeof(long);
    var actual = ConvertValue(23, expected);
    Assert.AreEqual(expected, actual.GetType());
}

[TestMethod]
public void Should_Convert_Long_To_Int() {
    var expected = typeof(int);
    var actual = ConvertValue((long)23, expected);
    Assert.AreEqual(expected, actual.GetType());
}

[TestMethod]
public void Should_Convert_String_To_Long() {
    var expected = typeof(long);
    var actual = ConvertValue("23", expected);
    Assert.AreEqual(expected, actual.GetType());
}

[TestMethod]
public void Should_Convert_String_To_Int() {
    var expected = typeof(int);
    var actual = ConvertValue("23", expected);
    Assert.AreEqual(expected, actual.GetType());
}
...