[c#] 반사와 함께 ‘주조’

다음 샘플 코드를 고려하십시오.

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

이제 반사를 통해 비슷한 작업을 수행해야합니다.

void SetValue(PropertyInfo info, object instance, object value)
{
    // throws System.ArgumentException: Decimal can not be converted to Int64
    info.SetValue(instance, value)
}

PropertyInfo가 항상 long을 나타내고 그 값이 항상 10 진수가 아니라고 가정 할 수는 없습니다. 그러나 값이 해당 속성에 대한 올바른 유형으로 캐스팅 될 수 있음을 알고 있습니다.

리플렉션을 통해 ‘value’매개 변수를 PropertyInfo 인스턴스가 나타내는 유형으로 어떻게 변환 할 수 있습니까?



답변

void SetValue(PropertyInfo info, object instance, object value)
{
    info.SetValue(instance, Convert.ChangeType(value, info.PropertyType));
}


답변

Thomas 대답은 IConvertible 인터페이스를 구현하는 유형에서만 작동합니다.

변환이 성공하려면 값이 IConvertible 인터페이스를 구현해야합니다. 메서드가 적절한 IConvertible 메서드에 대한 호출을 단순히 래핑하기 때문입니다. 이 메서드를 사용하려면 값을 conversionType으로 변환 할 수 있어야합니다.

이 코드는 unboxing (필요한 경우) 및 변환을 수행하는 linq 표현식을 컴파일합니다.

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

결과 람다 식은 (TOut) (TIn) Data와 같습니다. 여기서 TIn은 원본 데이터의 유형이고 TOut은 주어진 유형입니다.


답변

Thomas의 대답은 옳지 만 Convert.ChangeType이 nullable 형식으로의 변환을 처리하지 않는다는 내 결과를 추가 할 것이라고 생각했습니다. nullable 형식을 처리하기 위해 다음 코드를 사용했습니다.

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

이 코드는 다음 확장 메서드를 사용합니다.

public static class TypeExtensions
{
  public static bool IsNullableType(this Type type)
  {
    return type.IsGenericType
    && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
  }


답변

jeroenh의 답변에 기여하여 Convert.ChangeType이 null 값과 충돌하므로 변환 된 값을 가져 오는 줄은 다음과 같아야합니다.

var convertedValue = value == null ? null : Convert.ChangeType(value, targetType);


답변

Type이 Nullable Guid이면 위에 제안 된 솔루션 중 어느 것도 작동하지 않습니다. ‘ System.DBNull‘ 에서 ‘ ‘로의 잘못된 캐스트 System.Guid예외가 발생했습니다.Convert.ChangeType

변경 사항을 다음으로 수정하려면 :

var convertedValue = value == System.DBNull.Value ? null : Convert.ChangeType(value, targetType);


답변

이것은 매우 오래된 질문이지만 ASP.NET Core Google 직원을 위해 차임 할 것이라고 생각했습니다.

ASP.NET Core에서는 .IsNullableType()다른 변경 사항과 함께 보호되므로 코드가 약간 다릅니다. 다음은 ASP.NET Core에서 작동하도록 수정 된 @jeroenh의 답변입니다.

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


답변