'System.Int32'den' System.Nullable`1'e geçersiz yayın [[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

Yukarıdaki kodda InvalidCastException alıyorum. Yukarıdakiler için basitçe yazabilirim int? nVal = val, ancak yukarıdaki kod dinamik olarak çalışıyor.

Bir nesneye (burada val) sarılmış bir değer (int, float, vb. Gibi null yapılamayan türden) alıyorum ve onu başka bir türe dönüştürerek başka bir nesneye kaydetmem gerekiyor (bu da null yapılabilir sürüm olabilir veya olamaz) onun). Ne zaman

"System.Int32" den "System.Nullable" 1 [[System.Int32, mscorlib, Version = 4.0.0.0, Culture = nötr, PublicKeyToken = b77a5c561934e089]] 'a geçersiz atama.

An int, dönüştürülebilir / türe dönüştürülebilir olmalıdır nullable int, buradaki sorun nedir?


Sanırım coz Nullable<T>uygulamıyorIConvertible
V4Vendetta

2
Bu oldukça temeldir. Nullable özeldir, onu bir nesneye koyduğunuzda ya null olur ya da değer türünün kutulu bir değeri olur. Yani int mi istiyorsun? bir nesnede saklanmak mantıklı değil. Sadece int isteyin.
Hans Passant

Yanıtlar:


143

Nullable.GetUnderlyingTypeTemelini almak için kullanmalısınız Nullable.

Bu, sınırlamalarının üstesinden gelmek ChangeTypeiçin kullandığım yöntemNullable

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

genel olmayan yöntem:

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

yönteminizi kullanmak için, şöyle bir şey object nVal = ChangeType<int?>(val)yapmam gerekir : Burada yöntemi genel argüman (T) hakkında anlatmam gerekiyor, ancak elimde t(veya typeof (dataType)) var. Senaryomda ChangeType yönteminizi nasıl çağırırım?
Brij

1
Genel olmayan sürüm eklendi. Yardımı olup olmadığına bak.
gzaxx

Adresinde derleme hatası almak da default(conversion)benzer bir sorun gibi görünüyor.
Brij

Dikkat et @gzaxx ile aynı return nullşey değil default(T). Yapılarla uğraşıyorsanız, bunlar tamamen farklı şeylerdir.
Alex

Genel olmayan sürüm yapı ve ilkel türleri (nesneyi alır ve döndürürken) kutuya alır, bu nedenle boş döndürmek geçerlidir. Fonksiyonları arayan herkes bunu kendi başına halletmek zorunda kalacaktır.
gzaxx

9

Yukarıdakiler için basitçe int yazabilirim? nVal = val

Aslında bunu da yapamazsınız. 'Dan' objecta örtük bir dönüştürme yoktur Nullable<int>. Ancak , ' den' inte örtük bir dönüşüm vardır ,Nullable<int> böylece bunu yazabilirsiniz:

int? unVal = (int)val;

Nullable.GetUnderlyingTypeYöntemi kullanabilirsiniz .

Belirtilen boş değer atanabilir türün temelindeki tür bağımsız değişkenini döndürür .

Genel bir tür tanımı, bir tür parametresi listesi içeren Nullable gibi bir tür bildirimidir ve tür parametresi listesi bir veya daha fazla tür parametresi bildirir. Kapalı genel bir tür, bir tür parametresi için belirli bir türün belirtildiği bir tür bildirimidir.

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

İşte bir DEMO.


2

Sanırım işlevin neden çalışmadığını açıklamalıyım:

1- İstisnayı veren satır aşağıdaki gibidir:

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

aslında Convert.ConvertTypes dizisindeki işlev arama bundan sonra targer'ın bir Enum olup olmadığını görür ve hiçbir şey bulunamadığında yukarıdaki istisnayı atar.

2- Convert.ConvertTypes şu şekilde başlatılır:

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

Dolayısıyla int?, ConvertTypes dizisinde olmadığından ve Enum olmadığından, istisna atılır.

Devam etmek için, Convert.ChnageType işlevinin çalışması için elinizde:

  1. Dönüştürülecek nesne IConvertible

  2. Hedef türü ConvertTypes içinde ve Emptyne de değil DBNull(Atma istisnası olan bu ikisinde açık bir test var)

Bu davranışın nedeni int(ve diğer tüm varsayılan türlerin) Convert.DefaultToTypeIConvertibale olarak kullanılmasıdır.ToType implementation. and here is the code of theDefaultToType extractedusingILSpy

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

diğer yandan, cast Nullable sınıfının kendisi tarafından gerçekleştirilir ve tanım şu şekildedir:

public static implicit operator T?(T value)
{
    return new T?(value);
}
public static explicit operator T(T? value)
{
    return value.Value;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.