2009-07-06 26 views
251

Tôi muốn đặt thuộc tính của đối tượng thông qua Phản chiếu, với giá trị loại string. Ví dụ: Giả sử tôi có lớp Ship, với thuộc tính là Latitude, là double.Đặt thuộc tính bằng cách phản ánh với giá trị chuỗi

Dưới đây là những gì tôi muốn làm:

Ship ship = new Ship(); 
string value = "5.5"; 
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude"); 
propertyInfo.SetValue(ship, value, null); 

Như là, này ném một ArgumentException:

Object kiểu 'System.String' không thể được chuyển đổi sang loại 'System. Gấp đôi'.

Làm cách nào để chuyển đổi giá trị thành loại phù hợp, dựa trên propertyInfo?

+1

Câu hỏi dành cho bạn: đây có phải là một phần của giải pháp ORM tùy chỉnh không? – user3308043

Trả lời

419

Bạn có thể sử dụng Convert.ChangeType() - Nó cho phép bạn sử dụng thông tin thời gian chạy trên bất kỳ loại IConvertible nào để thay đổi định dạng biểu diễn. Tuy nhiên, không phải tất cả chuyển đổi đều có thể và bạn có thể cần viết logic trường hợp đặc biệt nếu bạn muốn hỗ trợ chuyển đổi từ các loại không phải là IConvertible.

Mã tương ứng (không xử lý ngoại lệ hoặc trường hợp luận lý đặc biệt) sẽ là:

Ship ship = new Ship(); 
string value = "5.5"; 
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude"); 
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null); 
31

Như một số người khác đã nói, bạn muốn sử dụng Convert.ChangeType:

propertyInfo.SetValue(ship, 
    Convert.ChangeType(value, propertyInfo.PropertyType), 
    null); 

Trong thực tế, tôi khuyên bạn nên nhìn vào toàn bộ Convert Class.

Lớp này và nhiều lớp hữu ích khác là một phần của System Namespace. Tôi thấy hữu ích khi quét không gian tên đó mỗi năm hoặc lâu hơn để xem những tính năng nào tôi đã bỏ lỡ. Hãy thử một lần!

+1

OP có lẽ muốn câu trả lời chung, để thiết lập thuộc tính của bất kỳ loại nào có chuyển đổi rõ ràng từ một chuỗi. –

+0

Điểm tốt. Tôi sẽ chỉnh sửa và chỉ vào những người trả lời thực sự, hoặc sẽ xóa tôi nếu ai đó sẽ thêm những gì tôi đã nói về phần còn lại của không gian tên. –

3

Hoặc bạn có thể thử:

propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null); 

//But this will cause problems if your string value IsNullOrEmplty... 
6

Bạn đang có lẽ tìm kiếm các phương pháp Convert.ChangeType. Ví dụ:

Ship ship = new Ship(); 
string value = "5.5"; 
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude"); 
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null); 
5

Sử dụng Convert.ChangeType và nhận loại chuyển đổi từ PropertyInfo.PropertyType.

propertyInfo.SetValue(ship, 
         Convert.ChangeType(value, propertyInfo.PropertyType), 
         null); 
-6

Bạn đang tìm cách chơi với Reflection hoặc bạn đang tìm cách xây dựng một phần mềm sản xuất? Tôi sẽ hỏi tại sao bạn đang sử dụng sự phản chiếu để thiết lập một thuộc tính.

Double new_latitude; 

Double.TryParse (value, out new_latitude); 
ship.Latitude = new_latitude; 
+0

Bạn nên tôn trọng những gì mọi người cố gắng làm và không phải những gì bạn nghĩ rằng họ phải làm. Đã bỏ phiếu. (Từ 'GenericProgramming.exe: ReflectionBenefits()') –

18

tôi nhận thấy có rất nhiều người được giới thiệu Convert.ChangeType - Đây không làm việc cho một số trường hợp tuy nhiên ngay sau khi bạn bắt đầu tham gia nullable loại bạn sẽ bắt đầu nhận InvalidCastExceptions:

http://weblogs.asp.net/pjohnson/archive/2006/02/07/Convert.ChangeType-doesn_2700_t-handle-nullables.aspx

Một wrapper đã được viết cách đây vài năm để xử lý điều này nhưng điều đó cũng không hoàn hảo.

http://weblogs.asp.net/pjohnson/archive/2006/02/07/Convert.ChangeType-doesn_2700_t-handle-nullables.aspx

11

Bạn có thể sử dụng một bộ chuyển đổi loại (không kiểm tra lỗi):

Ship ship = new Ship(); 
string value = "5.5"; 
var property = ship.GetType().GetProperty("Latitude"); 
var convertedValue = property.Converter.ConvertFrom(value); 
property.SetValue(self, convertedValue); 

Về mặt tổ chức mã, bạn có thể tạo một kind-of mixin rằng sẽ cho kết quả trong mã như này:

Ship ship = new Ship(); 
ship.SetPropertyAsString("Latitude", "5.5"); 

Điều này sẽ là đau có mã này:

public interface MPropertyAsStringSettable { } 
public static class PropertyAsStringSettable { 
    public static void SetPropertyAsString(
    this MPropertyAsStringSettable self, string propertyName, string value) { 
    var property = TypeDescriptor.GetProperties(self)[propertyName]; 
    var convertedValue = property.Converter.ConvertFrom(value); 
    property.SetValue(self, convertedValue); 
    } 
} 

public class Ship : MPropertyAsStringSettable { 
    public double Latitude { get; set; } 
    // ... 
} 

MPropertyAsStringSettable có thể được sử dụng lại cho nhiều lớp khác nhau.

Bạn cũng có thể tạo riêng của bạn type converters để đính kèm vào tài sản hoặc lớp học của bạn:

public class Ship : MPropertyAsStringSettable { 
    public Latitude Latitude { get; set; } 
    // ... 
} 

[TypeConverter(typeof(LatitudeConverter))] 
public class Latitude { ... } 
+0

Có lý do cụ thể nào khiến bạn thêm vào điểm đánh dấu insterface thay vì chỉ sử dụng 'đối tượng' không? – Groo

+0

Có, giao diện điểm đánh dấu đóng vai trò như một trình giữ chỗ để thêm các phương thức mở rộng vào. Sử dụng 'đối tượng' sẽ thêm các phương thức mở rộng cho tất cả các lớp, mà thường không được mong muốn. –

2

Nếu bạn đang viết ứng dụng Metro, bạn nên sử dụng mã khác:

Ship ship = new Ship(); 
string value = "5.5"; 
PropertyInfo propertyInfo = ship.GetType().GetTypeInfo().GetDeclaredProperty("Latitude"); 
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType)); 

Lưu ý:

ship.GetType().GetTypeInfo().GetDeclaredProperty("Latitude"); 

thay vì

ship.GetType().GetProperty("Latitude"); 
3

Tôi đã thử câu trả lời từ LBushkin và nó hoạt động tốt, nhưng nó sẽ không hoạt động đối với các giá trị null và các trường không có giá trị. Vì vậy, tôi đã thay đổi nó thành:

propertyName= "Latitude"; 
PropertyInfo propertyInfo = ship.GetType().GetProperty(propertyName); 
if (propertyInfo != null) 
{ 
    Type t = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType; 
    object safeValue = (value == null) ? null : Convert.ChangeType(value, t); 
    propertyInfo.SetValue(ship, safeValue, null); 
}