'đúc' với sự phản chiếu


81

Hãy xem xét mã mẫu sau:

class SampleClass
{
    public long SomeProperty { get; set; }
}

public void SetValue(SampleClass instance, decimal value)
{
    // value is of type decimal, but is in reality a natural number => cast
    instance.SomeProperty = (long)value;
}

Bây giờ tôi cần làm điều gì đó tương tự thông qua phản ánh:

void SetValue(PropertyInfo info, object instance, object value)
{
    // throws System.ArgumentException: Decimal can not be converted to Int64
    info.SetValue(instance, value)  
}

Lưu ý rằng tôi không thể giả định rằng PropertyInfo luôn đại diện cho một giá trị dài, không phải giá trị đó luôn là số thập phân. Tuy nhiên, tôi biết rằng giá trị có thể được chuyển thành đúng loại cho thuộc tính đó.

Làm cách nào để tôi có thể chuyển đổi tham số 'value' thành kiểu được đại diện bởi cá thể PropertyInfo thông qua phản chiếu?

Câu trả lời:


133
void SetValue(PropertyInfo info, object instance, object value)
{
    info.SetValue(instance, Convert.ChangeType(value, info.PropertyType));
}

1
Lưu ý rằng Convert.ChangeType(value, property.PropertyType);vẫn có thể thất bại nếu valuekhông triển khai IConvertiblegiao diện. Ví dụ, nếu info.PropertyTypelà một sốIEnumerable
derekantrican

42

Câu trả lời Thomas chỉ hoạt động cho các loại triển khai giao diện IConvertible:

Để chuyển đổi thành công, giá trị phải triển khai giao diện IConvertible, vì phương thức này chỉ đơn giản kết thúc một cuộc gọi đến một phương thức IConvertible thích hợp. Phương pháp này yêu cầu hỗ trợ chuyển đổi giá trị thành Loại chuyển đổi.

Mã này biên dịch một biểu thức linq thực hiện việc mở hộp (nếu cần) và chuyển đổi:

    public static object Cast(this Type Type, object data)
    {
        var DataParam = Expression.Parameter(typeof(object), "data");
        var Body = Expression.Block(Expression.Convert(Expression.Convert(DataParam, data.GetType()), Type));

        var Run = Expression.Lambda(Body, DataParam).Compile();
        var ret = Run.DynamicInvoke(data);
        return ret;
    }

Biểu thức lambda kết quả bằng (TOut) (TIn) Dữ liệu trong đó TIn là kiểu dữ liệu gốc và TOut là kiểu đã cho


2
Đây thực sự là câu trả lời mà tôi đang tìm kiếm. Đúc động không IConvertible.
jnm2

1
Heh tôi sẽ- nếu tôi là OP.
jnm2

1
Tôi hy vọng điều này sẽ cứu tôi khi cố gắng truyền IEnumerable<object>(nơi những đối tượng đó là chuỗi) IEnumerable<string>. Thật không may, tôi nhận được các lỗi nhưUnable to cast object of type 'System.Collections.Generic.IEnumerable'1[System.Object]' to type 'System.Collections.Generic.IEnumerable'1[System.String]'.
derekantrican

41

Câu trả lời của Thomas là đúng, nhưng tôi nghĩ rằng tôi sẽ thêm phát hiện của mình rằng Convert.ChangeType không xử lý chuyển đổi thành các kiểu nullable. Để xử lý các kiểu nullable, tôi đã sử dụng mã sau:

void SetValue(PropertyInfo info, object instance, object value)
{
    var targetType = info.PropertyType.IsNullableType() 
         ? Nullable.GetUnderlyingType(info.PropertyType) 
         : info.PropertyType; 
    var convertedValue = Convert.ChangeType(value, targetType);

    info.SetValue(instance, convertedValue, null);
}

Mã này sử dụng phương thức mở rộng sau:

public static class TypeExtensions
{
  public static bool IsNullableType(this Type type)
  {
    return type.IsGenericType 
    && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
  }

10

Đóng góp vào câu trả lời của jeroenh, tôi sẽ thêm rằng Convert.ChangeType bị lỗi với giá trị null, vì vậy dòng để nhận giá trị được chuyển đổi phải là:

var convertedValue = value == null ? null : Convert.ChangeType(value, targetType);

2

Khi Loại là Hướng dẫn Không có hiệu lực thì không có giải pháp đề xuất nào ở trên hoạt động. Truyền từ ngoại lệ ' System.DBNull' sang ' System.Guid' không hợp lệ được ném vàoConvert.ChangeType

Để sửa thay đổi đó thành:

var convertedValue = value == System.DBNull.Value ? null : Convert.ChangeType(value, targetType);

2
Vấn đề này không phải riêng đối với Guid mà là do thực tế là bạn nhận được DBNull.Valuethay vì chỉ đơn giản nullkhi tìm nạp các giá trị null từ cơ sở dữ liệu thông qua ADO.Net. Ví dụ, bạn sẽ thấy điều tương tự với nullable int.
jeroenh 27/02/13

0

Đây là một câu hỏi rất cũ nhưng tôi nghĩ rằng mình sẽ tham gia cho ASP.NET Core Googlers.

Trong ASP.NET Core, .IsNullableType()được bảo vệ (trong số các thay đổi khác) nên mã có một chút khác biệt. Đây là câu trả lời của @ jeroenh đã được sửa đổi để hoạt động trong ASP.NET Core:

void SetValue(PropertyInfo info, object instance, object value)
{
    Type proptype = info.PropertyType;
    if (proptype.IsGenericType && proptype.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
    {
        proptype = new NullableConverter(info.PropertyType).UnderlyingType;
    }

    var convertedValue = Convert.ChangeType(value, proptype);
    info.SetValue(instance, convertedValue);
}
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.