Đặt thuộc tính đối tượng bằng phản xạ


323

Có cách nào trong C # nơi tôi có thể sử dụng sự phản chiếu để đặt thuộc tính đối tượng không?

Ví dụ:

MyObject obj = new MyObject();
obj.Name = "Value";

Tôi muốn thiết lập obj.Namevới sự phản ánh. Cái gì đó như:

Reflection.SetProperty(obj, "Name") = "Value";

Có cách nào để làm điều này?

Câu trả lời:


391

Có, bạn có thể sử dụng Type.InvokeMember():

using System.Reflection;
MyObject obj = new MyObject();
obj.GetType().InvokeMember("Name",
    BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty,
    Type.DefaultBinder, obj, "Value");

Điều này sẽ đưa ra một ngoại lệ nếu objkhông có thuộc tính được gọi Namehoặc không thể được đặt.

Một cách tiếp cận khác là lấy siêu dữ liệu cho thuộc tính và sau đó đặt nó. Điều này sẽ cho phép bạn kiểm tra sự tồn tại của tài sản và xác minh rằng nó có thể được đặt:

using System.Reflection;
MyObject obj = new MyObject();
PropertyInfo prop = obj.GetType().GetProperty("Name", BindingFlags.Public | BindingFlags.Instance);
if(null != prop && prop.CanWrite)
{
    prop.SetValue(obj, "Value", null);
}

73
Nếu bạn không xử lý tất cả các chuỗi, trước tiên bạn có thể muốn chuyển đổi dữ liệu: var val = Convert.ChangeType(propValue, propInfo.PropertyType); nguồn: devx.com/vb2themax/Tip/19599
LostNomad311

4
cách khác, bạn có thể sử dụngobj.GetType().GetProperty("Name")?.GetSetMethod()?.Invoke(...)
tecfield

1
Không thể đặt giá trị cho CanWrite=Falsecác loại, phải không?
T.Todua

287

Bạn cũng có thể làm:

Type type = target.GetType();

PropertyInfo prop = type.GetProperty("propertyName");

prop.SetValue (target, propertyValue, null);

trong đó mục tiêu là đối tượng sẽ có tập thuộc tính của nó.


11
Tôi đã làm điều này rất giống ngày hôm nay. Các công việc trên rất tuyệt vời, rõ ràng việc kiểm tra null nên được thực hiện trên prop trước khi thử sử dụng nó.
Antony Scott

3
@AntonyScott Tôi sẽ nghĩ rằng bạn muốn biết nếu bạn đang gọi sai tài sản, vì vậy "thất bại trong âm thầm" có vẻ như là một khóa học tồi.
Jih

3
@jih Tôi có thể thấy quan điểm của bạn, nhưng nó phụ thuộc vào tình huống thực sự.
Antony Scott

94

Phản xạ, về cơ bản, tức là

myObject.GetType().GetProperty(property).SetValue(myObject, "Bob", null);

hoặc có các thư viện để giúp cả về sự thuận tiện và hiệu suất; ví dụ với FastMember :

var wrapped = ObjectAccessor.Create(obj); 
wrapped[property] = "Bob";

(cũng có lợi thế là không cần biết trước liệu đó có phải là trường so với tài sản không)


Wow, có một chút bối rối từ việc hợp nhất, nhưng tôi đã tìm thấy câu trả lời của bạn một lần nữa! Cảm ơn bạn, bạn xứng đáng được 'chấp nhận', nhưng vì chủ đề của tôi được hợp nhất :( Cảm ơn một lần nữa!
Halfpastfour.am

@MarcGravell, tôi đã xem FastMember và nó khá thú vị. Có bắt đầu / hướng dẫn ở đâu đó cho chúng tôi chỉ là người phàm để sử dụng lib tuyệt vời này của bạn không?
Sudhanshu Mishra

Làm cách nào tôi có thể nhận được loại tài sản bằng FastMember?
Nói rằng Roohullah Allem

Người truy cập @Jahan => GetMembers => Thành viên => Loại
Marc Gravell

câu trả lời chính xác! Điểm hay của một oneliner là nó nhanh hơn để hiểu, vì không có người dùng nào đặt tên biến ở giữa mà chính bạn có thể không có ý nghĩa gì ..
Bastiaan

27

Hoặc bạn có thể bọc một lớp lót của Marc trong lớp mở rộng của riêng bạn:

public static class PropertyExtension{       

   public static void SetPropertyValue(this object obj, string propName, object value)
    {
        obj.GetType().GetProperty(propName).SetValue(obj, value, null);
    }
}

và gọi nó như thế này:

myObject.SetPropertyValue("myProperty", "myValue");

Để có biện pháp tốt, hãy thêm một phương thức để nhận giá trị thuộc tính:

public static object GetPropertyValue(this object obj, string propName)
{
        return obj.GetType().GetProperty(propName).GetValue (obj, null);
}

14

Có, sử dụng System.Reflection:

using System.Reflection;

...

    string prop = "name";
    PropertyInfo pi = myObject.GetType().GetProperty(prop);
    pi.SetValue(myObject, "Bob", null);

13

Sử dụng những thứ như thế này:

public static class PropertyExtension{       

   public static void SetPropertyValue(this object p_object, string p_propertyName, object value)
   {
    PropertyInfo property = p_object.GetType().GetProperty(p_propertyName);
    property.SetValue(p_object, Convert.ChangeType(value, property.PropertyType), null);
   }
}

hoặc là

public static class PropertyExtension{       

   public static void SetPropertyValue(this object p_object, string p_propertyName, object value)
   {
    PropertyInfo property = p_object.GetType().GetProperty(p_propertyName);
    Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
    object safeValue = (value == null) ? null : Convert.ChangeType(value, t);

    property.SetValue(p_object, safeValue, null);
   }
}

2
Phần mà bạn có được loại tài sản và sau đó đúc nó thực sự hữu ích cho tôi. Nó hoạt động như một say mê. Cảm ơn bạn
Marc

12

Bạn cũng có thể truy cập các trường bằng cách sử dụng simillar:

var obj=new MyObject();
FieldInfo fi = obj.GetType().
  GetField("Name", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(obj,value)

Với sự phản ánh, mọi thứ có thể là một cuốn sách mở :) Trong ví dụ của tôi, chúng tôi đang ràng buộc với một trường cấp độ cá nhân.


8

Bạn có thể thử điều này khi bạn muốn gán các thuộc tính hàng loạt của một Đối tượng từ một Đối tượng khác bằng tên Thuộc tính:

public static void Assign(this object destination, object source)
    {
        if (destination is IEnumerable && source is IEnumerable)
        {
            var dest_enumerator = (destination as IEnumerable).GetEnumerator();
            var src_enumerator = (source as IEnumerable).GetEnumerator();
            while (dest_enumerator.MoveNext() && src_enumerator.MoveNext())
                dest_enumerator.Current.Assign(src_enumerator.Current);
        }
        else
        {
            var destProperties = destination.GetType().GetProperties();
            foreach (var sourceProperty in source.GetType().GetProperties())
            {
                foreach (var destProperty in destProperties)
                {
                    if (destProperty.Name == sourceProperty.Name && destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
                    {
                        destProperty.SetValue(destination,     sourceProperty.GetValue(source, new object[] { }), new object[] { });
                        break;
            }
        }
    }
}

2
Xin chào và chào mừng đến với Stack Overflow. Vui lòng định dạng mã của bạn đúng cách và không chỉ bỏ nó trong bài viết của bạn, nó sẽ giúp người khác hiểu câu trả lời của bạn.
ThreeFx

0

Tôi vừa xuất bản một gói Nuget cho phép thiết lập không chỉ các Thuộc tính cấp đầu tiên mà cả các thuộc tính lồng nhau trong đối tượng đã cho ở bất kỳ độ sâu nào.

Đây là gói

Đặt giá trị của một thuộc tính của một đối tượng theo đường dẫn của nó từ gốc.

Đối tượng có thể là một đối tượng phức tạp và thuộc tính có thể là thuộc tính lồng nhau sâu đa cấp hoặc nó có thể là một thuộc tính ngay dưới gốc. ObjectWritersẽ tìm thuộc tính bằng tham số đường dẫn thuộc tính và cập nhật giá trị của nó. Đường dẫn thuộc tính là tên được thêm vào của các thuộc tính được truy cập từ gốc đến thuộc tính nút cuối mà chúng ta muốn đặt, được phân cách bằng tham số chuỗi ký tự.

Sử dụng:

Để thiết lập các thuộc tính trực tiếp dưới gốc đối tượng:

I E. LineItemlớp có một thuộc tính int được gọi làItemId

LineItem lineItem = new LineItem();

ObjectWriter.Set(lineItem, "ItemId", 13, delimiter: null);

Để thiết lập thuộc tính lồng nhau nhiều cấp dưới gốc đối tượng:

I E. Invitelớp có một thuộc tính được gọi State, trong đó có một thuộc tính được gọi Invite(thuộc loại Mời), có một thuộc tính được gọi Recipient, có một thuộc tính được gọiId .

Để làm cho mọi thứ thậm chí phức tạp hơn, Statetài sản không phải là một loại tham chiếu, nó là một struct.

Đây là cách bạn có thể đặt thuộc tính Id (thành giá trị chuỗi của triển vọng tầm nhìn) ở dưới cùng của cây đối tượng trong một dòng.

Invite invite = new Invite();

ObjectWriter.Set(invite, "State_Invite_Recipient_Id", "outlook", delimiter: "_");

0

Dựa trên đề xuất của MarcGravell, tôi đã xây dựng phương thức tĩnh sau. Phương pháp này thường gán tất cả các thuộc tính khớp từ đối tượng nguồn vào mục tiêu bằng FastMember

 public static void DynamicPropertySet(object source, object target)
    {
        //SOURCE
        var src_accessor = TypeAccessor.Create(source.GetType());
        if (src_accessor == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var src_members = src_accessor.GetMembers();
        if (src_members == null)
        {
            throw new ApplicationException("Could not fetch members!");
        }
        var src_class_members = src_members.Where(x => x.Type.IsClass && !x.Type.IsPrimitive);
        var src_class_propNames = src_class_members.Select(x => x.Name);
        var src_propNames = src_members.Except(src_class_members).Select(x => x.Name);

        //TARGET
        var trg_accessor = TypeAccessor.Create(target.GetType());
        if (trg_accessor == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var trg_members = trg_accessor.GetMembers();
        if (trg_members == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var trg_class_members = trg_members.Where(x => x.Type.IsClass && !x.Type.IsPrimitive);
        var trg_class_propNames = trg_class_members.Select(x => x.Name);
        var trg_propNames = trg_members.Except(trg_class_members).Select(x => x.Name);



        var class_propNames = trg_class_propNames.Intersect(src_class_propNames);
        var propNames = trg_propNames.Intersect(src_propNames);

        foreach (var propName in propNames)
        {
            trg_accessor[target, propName] = src_accessor[source, propName];
        }
        foreach (var member in class_propNames)
        {
            var src = src_accessor[source, member];
            var trg = trg_accessor[target, member];
            if (src != null && trg != null)
            {
                DynamicPropertySet(src, trg);
            }
        }
    }
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.