2010-10-28 24 views
11

Tôi có một số mã (trong đó hoạt động tốt) mà trông giống như sau:Làm cách nào tôi có thể sử dụng phản chiếu để chuyển đổi từ int sang thập phân?

 int integer = 42; 
     decimal? castTo = integer; 

Sau đó, tôi muốn làm một cái gì đó tương tự với phản ánh, với một số mã trông như thế này:

object value = source; // source was an int originally 
var parameters = new object[1];  
    ... 
    parameters[0] = value; 
    var setMethod = property.GetSetMethod();  
    // Call the set method, which takes a decimal? as a parameter 
    setMethod.Invoke(o, parameters); 

Khi Tôi thực hiện việc này, tôi nhận được:

failed: System.ArgumentException : Object of type 'System.Int32' cannot be converted to type 'System.Nullable`1[System.Decimal]'. 
    at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr) 
    at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig) 
    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) 
    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) 
    at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) 

Tại sao chuyển đổi loại tiềm ẩn hoạt động tốt ở những nơi khác không phản ánh? Có một mẹo để sử dụng sự phản chiếu để thực hiện chuyển đổi này không?


Chỉnh sửa: Cảm ơn tất cả các câu trả lời. Đây là giải pháp tôi đã đưa ra, dựa trên những câu trả lời:

private object Convert(object source, Type destinationType) 
    { 
     if (source == null) 
     { 
      return null; 
     } 

     var sourceType = source.GetType(); 

     // unwrap nullable types 
     var nullableType = Nullable.GetUnderlyingType(destinationType); 
     if(nullableType != null) 
     { 
      destinationType = nullableType; 
     } 

     nullableType = Nullable.GetUnderlyingType(sourceType); 
     if(nullableType != null) 
     { 
      sourceType = nullableType; 
     } 


     var implicitCastMethod = 
      destinationType.GetMethod("op_Implicit", 
           new[] { sourceType }); 

     if(implicitCastMethod == null) 
     { 
      return null; 
     } 

     return implicitCastMethod.Invoke(null, new[] { source }); 
    } 

Chỉnh sửa # 2: Tôi muốn ai đó đã đề cập System.Convert.ChangeType(), mà xử lý những trường hợp này, và nhiều hơn nữa. Nó chỉ ra rằng op_Implicit chỉ có thể chuyển đổi thành kiểu số ít hạn chế hơn. (của khóa học, do đó "Ẩn" trong tên). Nói cách khác, giải pháp đầu tiên hoạt động cho intdecimal? nhưng không phải decimal?int. (Có vẻ như rằng tôi sẽ cần phải thay đổi mã này cũng thử op_Explicit nếu diễn viên tiềm ẩn thất bại, nếu tôi muốn để có thể xử lý một chuyển đổi từ decimal? trở lại int.)

Kể từ System.Convert.ChangeType() không làm việc với Nullable<> loại, cuối cùng tôi đã kết thúc bằng một số mã tương tự như những gì tôi tìm thấy here (chút thay đổi):

private static object Convert(object source, Type destinationType) 
    { 
     if(destinationType == null) 
     { 
      throw new ArgumentNullException("destinationType"); 
     } 

     if(destinationType.IsGenericType && 
      destinationType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) 
     { 
      if (source == null) 
      { 
       return null; 
      } 
      destinationType = Nullable.GetUnderlyingType(destinationType);     
     } 

     return System.Convert.ChangeType(source, destinationType); 


    } 

Trả lời

5

Bạn sẽ phải làm việc chuyển đổi chính mình, như trình biên dịch xử lý các diễn viên trong một môi trường không phản ánh. Khi mã phản chiếu về cơ bản là đánh giá các kiểu và các đối tượng như trình biên dịch, bạn sẽ phải tìm một phương thức có tên là op_implicit với các tham số cần thiết (trong trường hợp của bạn Int32) trên đối tượng của bạn và gọi nó. Sau đó, bạn có thể gọi trình truy cập thuộc tính. Một cách có thể sẽ là như sau:

//search for an implicit cast operator on the target type 
MethodInfo[] methods = targetType.GetMethods(); 
foreach(MethodInfo method = source.GetType().GetMethod("op_Implicit")) 
{ 
    if (method.Name == "op_Implicit") 
    { 
    ParameterInfo[] parameters = method.GetParameters(); 
    if (parameters.Length == 1 && parameters[0].ParameterType == value.GetType()) 
    { 
     value = method.Invoke(obj,new object[]{value}); 
     break; 
    } 
    } 
} 
+0

Đây là những gì cơ bản làm việc cho tôi. Ít nhất nó đã cho tôi đi đúng hướng. Cảm ơn! – mpontillo

+0

'MethodInfo method = source.GetType(). GetMethod (" op_Implicit ");' dễ đọc hơn tìm kiếm lặp lại rõ ràng. –

+0

@Merlyn, vâng, bạn sẽ nhận thấy đó là những gì tôi đã làm nếu bạn đọc chỉnh sửa đầu tiên của tôi, ngoại trừ tôi cũng đã thêm loại vào cuộc gọi GetMethod() là tốt. Nếu không, nếu có nhiều phương thức quá tải op_Implicit, tôi nghĩ rằng tôi vẫn sẽ phải lặp lại? – mpontillo

2

Why would an implicit type conversion that works fine elsewhere fail with reflection?

Vì không có chuyển đổi đó. Chuyển đổi ngầm định không có nghĩa là nó xảy ra tự động, khi bạn sử dụng nó trong mã trình biên dịch thêm mã cho nó.

Nếu bạn muốn sử dụng nó trong sự phản ánh, bạn phải làm như vậy, tức là tìm phương pháp tĩnh chuyển đổi từ loại này sang loại khác và gọi nó.

+0

Cảm ơn lời giải thích. Tôi đoán tôi đã hy vọng rằng sự phản chiếu sẽ có một cách dễ dàng hơn để làm điều đó, nhưng than ôi. – mpontillo

5

Thời gian chạy không biết về chuyển đổi tiềm ẩn.

Bạn có thể gọi op_Implicit hoặc phương pháp chuyển đổi khác thông qua phản ánh, nhưng theo cách đó bạn sẽ chỉ nhận được ngữ nghĩa chuyển đổi cụ thể mà bạn triển khai. Nếu bạn đang sử dụng C# 4.0, tôi khuyên bạn nên sử dụng loại "động" ở đây, vì nó sẽ tự động thực hiện các ngữ nghĩa chuyển đổi C#.

+3

+1 cho C# 4.0 'động' khuyến nghị –

+0

Cảm ơn vì điều này. Chúng tôi đang sử dụng C# 3.5, nhưng tôi tiếp tục thấy các tính năng C# 4.0 như thế này có vẻ tiện dụng. – mpontillo

1

Câu trả lời khác đã bao gồm lý do tại sao chuyển đổi ngầm không hoạt động. Nếu bạn cần chuyển đổi, hãy sử dụng một trong các câu trả lời khác. Trong trường hợp bạn không thực sự cần chuyển đổi là tiềm ẩn, đây là một lựa chọn đơn giản hơn:

class Test 
{ 
    public decimal? Val { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
    object o = new Test(); 
    object source = 5; 
    var setMethod = typeof(Test).GetProperty("Val").GetSetMethod(); 
    // Just do the cast explicitly 
    setMethod.Invoke(o, new object[] { (decimal?)(int)source }); 
    } 
} 

Lưu ý rằng nếu bạn đang thiếu (decimal?) cast, bạn nhận được lỗi rằng vấn đề gốc trích dẫn. Nếu bạn bỏ lỡ số (int) truyền, bạn sẽ gặp phải lỗi này:

Unhandled Exception: System.InvalidCastException: Specified cast is not valid. 
    at Program.Main(String[] args) in ...\ConsoleApplication1\Program.cs:line 14 
+0

Ý tưởng mới lạ; nó khiến tôi cười khúc khích ít nhất. Tôi đoán tôi nên nói điều này trong câu hỏi, nhưng mục tiêu thực sự là tạo ra động lực này; Tôi không phải lúc nào cũng làm (thập phân?) (Int). =) – mpontillo

+0

@ Giống như: Tôi đã tìm được nhiều, nhưng bạn không thể đánh giá trải nghiệm với một câu hỏi duy nhất trên internet. Nó sẽ không được 100% rõ ràng cho một newbie mà bạn phải cast * hai lần * để có được điều này để làm việc. Tôi sẽ để lại câu trả lời này, trong trường hợp nó có ích cho một người ít có kỹ năng hơn, người vẫn gặp phải lỗi tương tự. –

Các vấn đề liên quan