2013-08-02 47 views
58
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 ở mã trên. Ở trên tôi có thể chỉ cần viết int? nVal = val, nhưng ở trên mã đang thực thi động.Truyền không hợp lệ từ 'System.Int32' sang 'System.Nullable`1 [[System.Int32, mscorlib]]

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

dàn diễn viên không hợp lệ từ 'System.Int32' thành 'System.Nullable`1 [[System.Int32, mscorlib, Version = 4.0.0.0, Culture = trung tính, PublicKeyToken = b77a5c561934e089]]'.

Một int, nên được chuyển đổi/loại-cast để nullable int, vấn đề ở đây là gì?

+0

Tôi đoán có thể coz 'Nullable ' không thực hiện 'IConvertible' – V4Vendetta

+2

Điều này là 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 cho một int? được lưu trữ trong một đối tượng không có ý nghĩa. Chỉ cần yêu cầu int. –

Trả lời

87

Bạn phải sử dụng Nullable.GetUnderlyingType để nhận loại cơ bản Nullable.

Đây là phương pháp tôi sử dụng để khắc phục hạn chế của ChangeType cho 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); 
} 

phi phương pháp 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); 
} 
+0

để người dùng phương pháp của bạn, tôi sẽ cần phải làm một cái gì đó như: 'đối tượng nVal = ChangeType (val)', ở đây tôi cần phải nói phương pháp về đối số chung (T), nhưng tôi có 't' (hoặc typeof (dataType)) theo ý của tôi. Làm thế nào tôi sẽ gọi phương thức ChangeType của bạn trong kịch bản của tôi? – Brij

+1

Đã thêm phiên bản không phải là phiên bản chung. Xem nếu nó giúp. – gzaxx

+0

Bắt lỗi biên dịch tại 'mặc định (chuyển đổi)', có vẻ như vấn đề tương tự. – Brij

3

Ở trên tôi chỉ cần viết int? nVal = val

Thực ra, bạn cũng không thể làm điều đó. Không có chuyển đổi tiềm ẩn từ object đến Nullable<int>. Nhưng có một chuyển đổi ngầm từ int để Nullable<int> vì vậy bạn có thể viết này:

int? unVal = (int)val; 

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

Trả lại đối số kiểu cơ bản của loại có thể xác định không được chỉ định.

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

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 

Dưới đây là một DEMO.

1

Tôi nghĩ rằng tôi nên giải thích lý do tại sao chức năng 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 
    })); 

trên thực tế, chức năng tìm kiếm trong mảng Chuyển đổi.ConvertTypes sau đó nó thấy nếu targer là một Enum và khi không có gì được tìm thấy nó ném ngoại lệ ở trên.

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

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, kể từ khi int? không nằm trong mảng ConvertTypes và không phải là một Enum ngoại lệ được ném.

Vì vậy, để tiếp tục, cho hàm Convert.ChnageType để làm việc bạn có:

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

    loại
  2. Mục tiêu nằm trong ConvertTypes và không Empty cũng không DBNull (Có một thử nghiệm khám phá về hai trường hợp có ngoại lệ ném)

Hành vi này là do int (và tất cả các loại mặc định khác) sử dụng Convert.DefaultToType như IConvertibale.ToType implementation. and here is the code of the DefaultToType extracted sử dụng 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 
    })); 
} 

trong Mặt khác các diễn viên được thực hiện bởi lớp Nullable bản thân 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; 
} 
Các vấn đề liên quan