ตั้งค่าคุณสมบัติวัตถุโดยใช้การสะท้อน


323

มีวิธีใน C # ที่ฉันสามารถใช้การสะท้อนเพื่อตั้งค่าคุณสมบัติวัตถุหรือไม่

Ex:

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

ฉันต้องการตั้งค่าobj.Nameด้วยการสะท้อน สิ่งที่ต้องการ:

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

มีวิธีทำเช่นนี้หรือไม่?

คำตอบ:


391

ใช่คุณสามารถใช้Type.InvokeMember():

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

สิ่งนี้จะทำให้เกิดข้อยกเว้นหากobjไม่มีคุณสมบัติที่เรียกว่าNameหรือไม่สามารถตั้งค่าได้

อีกวิธีหนึ่งคือการรับข้อมูลเมตาสำหรับคุณสมบัติและจากนั้นตั้งค่า สิ่งนี้จะช่วยให้คุณตรวจสอบการมีอยู่ของคุณสมบัติและตรวจสอบว่าสามารถตั้งค่าได้:

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
หากคุณไม่ได้จัดการกับสตริงทั้งหมดคุณอาจต้องการแปลงข้อมูลเป็นอันดับแรก: var val = Convert.ChangeType(propValue, propInfo.PropertyType); แหล่งที่มา: devx.com/vb2themax/Tip/19599
LostNomad311

4
หรือคุณสามารถใช้obj.GetType().GetProperty("Name")?.GetSetMethod()?.Invoke(...)
tecfield

1
เป็นไปไม่ได้ที่จะตั้งค่าสำหรับCanWrite=Falseประเภทใช่ไหม
T.Todua

287

คุณยังสามารถทำสิ่งต่อไปนี้

Type type = target.GetType();

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

prop.SetValue (target, propertyValue, null);

โดยที่ target คือวัตถุที่จะมีชุดคุณสมบัติ


11
วันนี้ฉันได้ทำสิ่งเดียวกันนี้แล้ว ข้างต้นใช้งานได้ดีมากเห็นได้ชัดว่าการตรวจสอบ null ควรทำบนเสาก่อนที่จะพยายามใช้
แอนโทนีสก็อต

3
@ AntonyScott ฉันคิดว่าคุณต้องการทราบว่าคุณกำลังเรียกใช้คุณสมบัติที่ไม่ถูกต้องหรือไม่ดังนั้น "Fail silently" จึงดูเหมือนว่าเป็นหลักสูตรที่ไม่ดี
jih

3
@ jih ฉันเห็นจุดของคุณ แต่มันขึ้นอยู่กับสถานการณ์จริงๆ
Antony Scott

94

การสะท้อนกลับโดยทั่วไปคือ

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

หรือมีห้องสมุดเพื่อช่วยทั้งในด้านความสะดวกและประสิทธิภาพ ตัวอย่างเช่นกับFastMember :

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

(ซึ่งมีข้อได้เปรียบที่ไม่จำเป็นต้องทราบล่วงหน้าว่าเป็นฟิลด์เทียบกับทรัพย์สินหรือไม่)


ว้าวสับสนเล็กน้อยจากการรวมกัน แต่ฉันพบคำตอบของคุณอีกครั้ง! ขอบคุณคุณสมควรได้รับ 'ยอมรับ' แต่เนื่องจากมีการรวมเธรดของฉัน :( ขอบคุณอีกครั้ง!
halfpastfour.am

@ MarcGravell ฉันกำลังดู FastMember และมันน่าสนใจทีเดียว มีการเริ่มต้น / การสอนที่ไหนสักแห่งสำหรับพวกเราที่เป็นปุถุชนที่จะใช้ lib อันยอดเยี่ยมนี้ของคุณหรือไม่?
Sudhanshu Mishra

ฉันจะได้รับประเภทของทรัพย์สินโดย FastMember ได้อย่างไร
กล่าวว่า Roohullah Allem

@Jahan accessor => GetMembers => Member => ประเภท
Marc Gravell

คำตอบที่ดี! สิ่งที่ดีเกี่ยวกับ oneliner คือมันเร็วกว่าที่จะเข้าใจเนื่องจากไม่มีชื่อผู้ใช้ตัวแปรในระหว่างที่สำหรับตัวคุณเองอาจไม่ทำให้รู้สึกใด ๆ ..
Bastiaan

27

หรือคุณสามารถพันซับใน Marc ในคลาสเสริมของคุณเองก็ได้:

public static class PropertyExtension{       

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

และเรียกมันว่าสิ่งนี้:

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

สำหรับการวัดที่ดีลองเพิ่มวิธีการรับค่าคุณสมบัติ:

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


13

ใช้บางสิ่งเช่นนี้:

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

หรือ

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
ส่วนที่คุณได้รับคุณสมบัติประเภทแล้วโยนมันมีประโยชน์จริง ๆ สำหรับฉัน มันทำงานเหมือนจับใจ ขอบคุณ
Marc

12

คุณยังสามารถเข้าถึงฟิลด์โดยใช้ลักษณะ simillar:

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

ด้วยการสะท้อนทุกอย่างสามารถเป็นหนังสือเปิดได้ :) ในตัวอย่างของฉันเราผูกพันกับฟิลด์ระดับอินสแตนซ์ส่วนตัว


8

คุณสามารถลองใช้งานนี้เมื่อคุณต้องการกำหนดคุณสมบัติจำนวนมากของวัตถุจากวัตถุอื่นโดยใช้ชื่อคุณสมบัติ:

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
สวัสดีและยินดีต้อนรับสู่ Stack Overflow โปรดจัดรูปแบบรหัสของคุณอย่างถูกต้องและไม่เพียงทิ้งในโพสต์ของคุณมันจะช่วยให้ผู้อื่นเข้าใจคำตอบของคุณ
ThreeFx

0

ฉันเพิ่งเผยแพร่แพ็คเกจ Nuget ที่ช่วยให้การตั้งค่าไม่เพียง แต่คุณสมบัติระดับแรก แต่ยังมีคุณสมบัติที่ซ้อนกันในวัตถุที่กำหนดในทุกระดับความลึก

นี่คือแพคเกจ

ตั้งค่าของคุณสมบัติของวัตถุตามเส้นทางจากรูท

วัตถุสามารถเป็นวัตถุที่ซับซ้อนและคุณสมบัติสามารถเป็นหลายระดับซ้อนกันได้ในระดับลึกหรืออาจเป็นคุณสมบัติโดยตรงภายใต้ราก ObjectWriterจะค้นหาคุณสมบัติโดยใช้พารามิเตอร์เส้นทางคุณสมบัติและอัปเดตค่าของมัน พา ธ คุณสมบัติเป็นชื่อที่ต่อท้ายของคุณสมบัติที่เข้าชมตั้งแต่รูทจนถึงคุณสมบัติโหนดสุดท้ายที่เราต้องการตั้งค่าคั่นด้วยพารามิเตอร์สตริงตัวคั่น

การใช้งาน:

สำหรับการตั้งค่าคุณสมบัติโดยตรงภายใต้รูทวัตถุ:

กล่าวคือ LineItemclass มีคุณสมบัติ int ที่เรียกว่าItemId

LineItem lineItem = new LineItem();

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

สำหรับการตั้งค่าคุณสมบัติที่ซ้อนกันหลายระดับใต้รูทวัตถุ:

กล่าวคือ Inviteชั้นจะมีคุณสมบัติที่เรียกว่าStateซึ่งมีคุณสมบัติที่เรียกว่าInvite(จากเชิญชนิด) ซึ่งมีคุณสมบัติที่เรียกว่าซึ่งมีคุณสมบัติที่เรียกว่าRecipientId

เพื่อให้ได้สิ่งที่ซับซ้อนมากขึ้นที่สถานที่ให้บริการไม่ได้เป็นชนิดการอ้างอิงมันเป็นStatestruct

นี่คือวิธีที่คุณสามารถตั้งค่าคุณสมบัติ Id (เป็นค่าสตริงของ“ outlook”) ที่ด้านล่างของแผนผังวัตถุในบรรทัดเดียว

Invite invite = new Invite();

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

0

ขึ้นอยู่กับข้อเสนอแนะ MarcGravell ของผมได้สร้างวิธีการ method.The คงดังต่อไปนี้โดยทั่วไปกำหนดคุณสมบัติการจับคู่ทั้งหมดออกจากวัตถุต้นทางไปยังเป้าหมายโดยใช้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);
            }
        }
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.