'System.Int32'에서 'System.Nullable'1 [[System.Int32, mscorlib]] 로의 잘못된 캐스트


81
Type t = typeof(int?); //will get this dynamically
object val = 5; //will get this dynamically
object nVal = Convert.ChangeType(val, t);//getting exception here

위 코드에서 InvalidCastException이 발생합니다. 위의 경우 간단히 작성할 수 int? nVal = val있지만 위의 코드는 동적으로 실행됩니다.

객체 (여기서는 val)에 래핑 된 값 (int, float 등의 nullable이 아닌 유형)을 얻고 있으며 다른 유형으로 캐스팅하여 다른 객체에 저장해야합니다. 그것의). 언제

'System.Int32'에서 'System.Nullable`1 [[System.Int32, mscorlib, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089]]'로의 캐스트가 잘못되었습니다.

int,로 변환 / 형 - 스트해야합니다 nullable int, 여기에 문제가 무엇입니까?


나는 사촌 아마 추측 Nullable<T>구현하지 않습니다IConvertible
V4Vendetta

2
이것은 다소 근본적입니다. Nullable은 특별합니다. 객체에 넣으면 null이되거나 값 유형의 boxed 값이됩니다. 그래서 int를 요구합니까? 객체에 저장된 것은 의미가 없습니다. int를 요청하십시오.
Hans Passant 2013-08-02

답변:


140

Nullable.GetUnderlyingType기본 유형을 가져 오려면 을 사용해야 합니다 Nullable.

이것은 내가 한계를 극복하기 위해 사용하는 방법 ChangeType입니다.Nullable

public static T ChangeType<T>(object value) 
{
   var t = typeof(T);

   if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) 
   {
       if (value == null) 
       { 
           return default(T); 
       }

       t = Nullable.GetUnderlyingType(t);
   }

   return (T)Convert.ChangeType(value, t);
}

비 일반적인 방법 :

public static object ChangeType(object value, Type conversion) 
{
   var t = conversion;

   if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) 
   {
       if (value == null) 
       { 
           return null; 
       }

       t = Nullable.GetUnderlyingType(t);
   }

   return Convert.ChangeType(value, t);
}

귀하의 메서드를 사용하려면 다음과 같은 작업을 수행해야합니다. object nVal = ChangeType<int?>(val)여기에서는 제네릭 인수 (T)에 대한 메서드를 알려야하지만 원하는대로 t(또는 typeof (dataType))이 있습니다. 내 시나리오에서 ChangeType 메서드를 어떻게 호출합니까?
Brij

1
일반 버전이 추가되었습니다. 도움이되는지 확인하십시오.
gzaxx

에서 컴파일 오류가 발생하는 default(conversion)것은 비슷한 문제처럼 보입니다.
Brij

@gzaxx return nulldefault(T). 구조체를 다룰 때는 완전히 다른 것입니다.
Alex

제네릭이 아닌 버전은 박스 구조체 및 기본 유형 (객체를 가져오고 반환하기 때문에)이므로 null 반환이 유효합니다. 함수를 호출하는 사람은 누구나 이것을 스스로 처리해야합니다.
gzaxx

9

위의 경우 간단히 int를 작성할 수 있습니까? nVal = 발

사실, 당신도 그렇게 할 수 없습니다. 에서 object로의 암시 적 변환은 없습니다 Nullable<int>. 그러나이 있다 에서 암시 적 변환 intNullable<int>당신이 쓸 수 있도록이 :

int? unVal = (int)val;

Nullable.GetUnderlyingType방법 을 사용할 수 있습니다 .

기본 형식 인수를 반환합니다.지정된 nullable 형식 를 .

제네릭 형식 정의는 형식 매개 변수 목록을 포함하는 Nullable과 같은 형식 선언이며 형식 매개 변수 목록은 하나 이상의 형식 매개 변수를 선언합니다. 닫힌 제네릭 형식은 형식 매개 변수에 대해 특정 형식이 지정된 형식 선언입니다.

Type t = typeof(int?); //will get this dynamically
Type u = Nullable.GetUnderlyingType(t);
object val = 5; //will get this dynamically
object nVal = Convert.ChangeType(val, u);// nVal will be 5

여기에 DEMO.


2

기능이 작동하지 않는 이유를 설명해야한다고 생각합니다.

1- 예외를 발생시키는 줄은 다음과 같습니다.

throw new InvalidCastException(Environment.GetResourceString("InvalidCast_FromTo", new object[]
  {
    value.GetType().FullName, 
    targetType.FullName
    }));

실제로 배열 Convert.ConvertTypes에서 함수 검색은 targer가 Enum인지 확인하고 아무것도 발견되지 않으면 위의 예외를 throw합니다.

2- Convert.ConvertTypes는 다음과 같이 초기화됩니다.

Convert.ConvertTypes = new RuntimeType[]
   {
      (RuntimeType)typeof(Empty), 
      (RuntimeType)typeof(object), 
      (RuntimeType)typeof(DBNull), 
      (RuntimeType)typeof(bool), 
      (RuntimeType)typeof(char), 
      (RuntimeType)typeof(sbyte), 
      (RuntimeType)typeof(byte), 
      (RuntimeType)typeof(short), 
      (RuntimeType)typeof(ushort), 
      (RuntimeType)typeof(int), 
      (RuntimeType)typeof(uint), 
      (RuntimeType)typeof(long), 
      (RuntimeType)typeof(ulong), 
      (RuntimeType)typeof(float), 
      (RuntimeType)typeof(double), 
      (RuntimeType)typeof(decimal), 
      (RuntimeType)typeof(DateTime), 
      (RuntimeType)typeof(object), 
      (RuntimeType)typeof(string)
   };

따라서 int?가 ConvertTypes 배열에없고 Enum이 아니므로 예외가 발생합니다.

다시 시작하려면 Convert.ChnageType 함수가 작동하려면 다음을 수행하십시오.

  1. 변환 할 개체는 IConvertible입니다.

  2. 대상 유형이 ConvertTypes 내에 있으며 Empty또는 아닙니다 DBNull(두 가지 예외가 발생하는 경우 명시 적 테스트가 있습니다).

이 동작은 int(및 기타 모든 기본 유형)이 다음을 사용 Convert.DefaultToType하여 IConvertibale.ToType implementation. and here is the code of theDefaultToType으로 extracted사용하기 때문입니다.ILSpy

internal static object DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
{
    if (targetType == null)
    {
        throw new ArgumentNullException("targetType");
    }
    RuntimeType left = targetType as RuntimeType;
    if (left != null)
    {
        if (value.GetType() == targetType)
        {
            return value;
        }
        if (left == Convert.ConvertTypes[3])
        {
            return value.ToBoolean(provider);
        }
        if (left == Convert.ConvertTypes[4])
        {
            return value.ToChar(provider);
        }
        if (left == Convert.ConvertTypes[5])
        {
            return value.ToSByte(provider);
        }
        if (left == Convert.ConvertTypes[6])
        {
            return value.ToByte(provider);
        }
        if (left == Convert.ConvertTypes[7])
        {
            return value.ToInt16(provider);
        }
        if (left == Convert.ConvertTypes[8])
        {
            return value.ToUInt16(provider);
        }
        if (left == Convert.ConvertTypes[9])
        {
            return value.ToInt32(provider);
        }
        if (left == Convert.ConvertTypes[10])
        {
            return value.ToUInt32(provider);
        }
        if (left == Convert.ConvertTypes[11])
        {
            return value.ToInt64(provider);
        }
        if (left == Convert.ConvertTypes[12])
        {
            return value.ToUInt64(provider);
        }
        if (left == Convert.ConvertTypes[13])
        {
            return value.ToSingle(provider);
        }
        if (left == Convert.ConvertTypes[14])
        {
            return value.ToDouble(provider);
        }
        if (left == Convert.ConvertTypes[15])
        {
            return value.ToDecimal(provider);
        }
        if (left == Convert.ConvertTypes[16])
        {
            return value.ToDateTime(provider);
        }
        if (left == Convert.ConvertTypes[18])
        {
            return value.ToString(provider);
        }
        if (left == Convert.ConvertTypes[1])
        {
            return value;
        }
        if (left == Convert.EnumType)
        {
            return (Enum)value;
        }
        if (left == Convert.ConvertTypes[2])
        {
            throw new InvalidCastException(Environment.GetResourceString("InvalidCast_DBNull"));
        }
        if (left == Convert.ConvertTypes[0])
        {
            throw new InvalidCastException(Environment.GetResourceString("InvalidCast_Empty"));
        }
    }
    throw new InvalidCastException(Environment.GetResourceString("InvalidCast_FromTo", new object[]
    {
        value.GetType().FullName, 
        targetType.FullName
    }));
}

반면에 캐스트는 Nullable 클래스 자체에 의해 구현되며 정의는 다음과 같습니다.

public static implicit operator T?(T value)
{
    return new T?(value);
}
public static explicit operator T(T? value)
{
    return value.Value;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.