Truyền từ 'System.Int32' sang 'System.Nullable`1 không hợp lệ [[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

Tôi nhận được InvalidCastException trong mã trên. Đối với phần trên, tôi chỉ có thể viết int? nVal = val, nhưng đoạn mã trên đang thực thi động.

Tôi đang nhận được một giá trị (thuộc kiểu không thể nullable như int, float, v.v.) được bao bọc trong một đối tượng (ở đây là val) và tôi phải lưu nó vào một đối tượng khác bằng cách truyền nó sang một kiểu khác (có thể hoặc không thể là phiên bản có thể nullable của nó). Khi nào

Truyền không hợp lệ từ 'System.Int32' thành 'System.Nullable`1 [[System.Int32, mscorlib, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089]]'.

An int, nên có thể chuyển đổi / loại có thể chuyển thành nullable int, vấn đề ở đây là gì?


Tôi đoán có lẽ coz Nullable<T>không thực hiệnIConvertible
V4Vendetta

2
Đây là điều khá cơ bản. Nullable là đặc biệt, khi bạn đặt nó vào một đối tượng thì nó trở thành null hoặc trở thành một giá trị đóng hộp của kiểu giá trị. Vì vậy, yêu cầu một int? được lưu trữ trong một đối tượng chỉ không có ý nghĩa. Chỉ cần yêu cầu int.
Hans Passant

Câu trả lời:


140

Bạn phải sử dụng Nullable.GetUnderlyingTypeđể có được loại cơ bản của Nullable.

Đây là phương pháp tôi sử dụng để khắc phục hạn chế của ChangeTypechoNullable

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

phương pháp không chung chung:

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

để sử dụng phương thức của bạn, tôi sẽ cần phải làm điều gì đó như object nVal = ChangeType<int?>(val):, ở đây tôi cần cho phương thức biết về đối số chung (T), nhưng tôi có t(hoặc typeof (dataType)) theo ý của tôi. Tôi sẽ gọi phương thức ChangeType của bạn như thế nào trong trường hợp của tôi?
Brij

1
Đã thêm phiên bản không chung chung. Xem nếu nó giúp.
gzaxx

Gặp lỗi biên dịch tại default(conversion), có vẻ như vấn đề tương tự.
Brij

Cẩn thận @gzaxx như return nullkhông giống như default(T). Nếu bạn đang xử lý các cấu trúc thì chúng là những thứ hoàn toàn khác.
Alex

Phiên bản không chung chung sẽ đóng hộp các kiểu cấu trúc và nguyên thủy (khi nó nhận và trả về đối tượng) nên việc trả về null là hợp lệ. Bất kỳ ai gọi các hàm sẽ phải tự xử lý việc này.
gzaxx

9

Đối với ở trên, tôi chỉ có thể viết int? nVal = val

Trên thực tế, bạn cũng không thể làm điều đó. Không có chuyển đổi ngầm định từ objectsang Nullable<int>. Nhưng có một chuyển đổi ngầm từ intđể Nullable<int>, do đó bạn có thể viết này:

int? unVal = (int)val;

Bạn có thể sử dụng Nullable.GetUnderlyingTypephương pháp.

Trả về đối số kiểu cơ bản của kiểu nullable đã chỉ định.

Định nghĩa kiểu chung là một khai báo kiểu, chẳng hạn như Nullable, chứa danh sách tham số kiểu và danh sách tham số kiểu khai báo một hoặc nhiều tham số kiểu. Một kiểu chung đóng là một khai báo kiểu trong đó một kiểu cụ thể được chỉ định cho một tham số kiểu.

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

Đây là a DEMO.


2

Tôi nghĩ tôi nên giải thích tại sao hàm không hoạt động:

1- Dòng ném ngoại lệ như sau:

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

trong thực tế, hàm tìm kiếm trong mảng Convert.ConvertTypes sau đó nó sẽ xem liệu targer có phải là Enum hay không và khi không tìm thấy gì, nó sẽ ném ngoại lệ ở trên.

2- Convert.ConvertTypes được khởi tạo như sau:

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

Vì vậy, vì int?không có trong mảng ConvertTypes và không phải là Enum nên ngoại lệ sẽ được ném ra.

Vì vậy, để tiếp tục, để chức năng Convert.ChnageType hoạt động, bạn có:

  1. Đối tượng được chuyển đổi là IConvertible

  2. Loại đích nằm trong ConvertTypes chứ không phải Emptycũng không DBNull(Có một bài kiểm tra đầy đủ trên hai loại đó với ngoại lệ ném)

Hành vi này là vì int(và tất cả các loại mặc định khác) sử dụng Convert.DefaultToTypenhư IConvertibale.ToType implementation. and here is the code of theDefaultToType extractedsử dụngILSpy

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

mặt khác ép kiểu được thực hiện bởi chính lớp Nullable và định nghĩa là:

public static implicit operator T?(T value)
{
    return new T?(value);
}
public static explicit operator T(T? value)
{
    return value.Value;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.