Đặt thuộc tính bằng phản xạ với giá trị chuỗi


312

Tôi muốn đặt thuộc tính của một đối tượng thông qua Reflection, với giá trị loại string. Vì vậy, ví dụ, giả sử tôi có một Shiplớp, với một thuộc tính của Latitude, đó là a double.

Đâ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à, điều này ném một ArgumentException:

Đối tượng của loại 'System.String' không thể được chuyển đổi thành loại 'System.Double'.

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


1
Câu hỏi 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?
3308043

Câu trả lời:


527

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ỳ IConvertibleloại nào để thay đổi định dạng đại diện. Tuy nhiên, không phải tất cả các chuyển đổi đều có thể và bạn có thể cần phải 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 có IConvertible.

Mã tương ứng (không có xử lý ngoại lệ hoặc logic trường hợp đặ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);

Xem lại câu trả lời @AliKaraca dưới đây. Cả cái này và cái dưới đây đều nhanh và lỏng lẻo nhưng thực hiện công việc cho các loại phổ biến.
Aaron Hudon

TryChangeTypehay CanChangeTypekhông?
Shimmy Weitzhandler

34

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

Trên thực tế, tôi khuyên bạn nên xem xét toàn bộ ConvertLớp học .

Lớp này và nhiều lớp hữu ích khác là một phần của SystemKhông gian tên . 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 tôi đã bỏ lỡ. Hãy thử một lần!


1
OP có thể muốn câu trả lời chung, để đặt 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.
Daniel Earwicker

Đ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.
John Saunders

19

Tôi nhận thấy rất nhiều người đang khuyến nghị Convert.ChangeType- Điều này có hiệu quả đối với một số trường hợp tuy nhiên ngay khi bạn bắt đầu liên quan đến nullablecác loại bạn sẽ bắt đầu nhận được InvalidCastExceptions:

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

Một trình bao bọc đã được viết cách đây vài năm để xử lý việc 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


13

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

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

Tôi phải nói lời cảm ơn khi tôi gặp trường hợp này và đây là giải pháp duy nhất. cảm ơn ~
Franva

11

Bạn có thể sử dụng trình 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 loại mixin sẽ dẫn đến mã như thế này:

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

Điều này sẽ đạt được với 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 tái sử dụng cho nhiều lớp khác nhau.

Bạn cũng có thể tạo các trình chuyển đổi loại tùy chỉnh của riêng mình để đính kèm vào các thuộc tính hoặc các lớp của bạn:

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

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

Có bất kỳ lý do cụ thể tại sao bạn thêm dấu insterface thay vì chỉ sử dụng objectkhông?
Groo

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

6

Có lẽ bạn đang tìm kiếm Convert.ChangeTypephương pháp. 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.ChangeTypevà nhận loại để chuyển đổi từ PropertyInfo.PropertyType.

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

4

Tôi sẽ trả lời điều này với một câu trả lời chung chung. Thông thường những câu trả lời không hoạt động với hướng dẫn. Đây là một phiên bản làm việc với hướng dẫn quá.

var stringVal="6e3ba183-89d9-e611-80c2-00155dcfb231"; // guid value as string to set
var prop = obj.GetType().GetProperty("FooGuidProperty"); // property to be setted
var propType = prop.PropertyType;

// var will be type of guid here
var valWithRealType = TypeDescriptor.GetConverter(propType).ConvertFrom(stringVal); 

1
Đây phải là câu trả lời được chấp nhận. Nó cũng hoạt động với GUID <3. Cảm ơn, Ali (đó là biệt danh của con gái tôi)
Cătălin Rădoi

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...

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

Ghi chú:

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

thay vì

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

0

Sử dụng mã sau đây sẽ giải quyết vấn đề của bạn:

item.SetProperty(prop.Name, Convert.ChangeType(item.GetProperty(prop.Name).ToString().Trim(), prop.PropertyType));

-9

Bạn đang tìm cách chơi xung quanh 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 sử dụng sự phản chiếu để thiết lập một tài sản.

Double new_latitude;

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

1
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ĩ họ phải làm. Bị hạ bệ. (Từ GenericProgramming.exe:ReflectionBenefits())
Петър Петров
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.