Setel properti objek menggunakan refleksi

323

Apakah ada cara di C # di mana saya bisa menggunakan refleksi untuk mengatur properti objek?

Ex:

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

Saya ingin mengatur obj.Namedengan refleksi. Sesuatu seperti:

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

Apakah ada cara untuk melakukan ini?

Melursus
sumber

Jawaban:

391

Ya, Anda bisa menggunakan Type.InvokeMember():

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

Ini akan mengeluarkan pengecualian jika objtidak memiliki properti yang dipanggil Name, atau tidak dapat diatur.

Pendekatan lain adalah mendapatkan metadata untuk properti, dan kemudian mengaturnya. Ini akan memungkinkan Anda untuk memeriksa keberadaan properti, dan memverifikasi bahwa itu dapat diatur:

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);
}
Andy
sumber
73
Jika Anda tidak berurusan dengan semua string, Anda mungkin ingin mengonversi data terlebih dahulu: var val = Convert.ChangeType(propValue, propInfo.PropertyType); sumber: devx.com/vb2themax/Tip/19599
LostNomad311
4
sebagai alternatif, Anda dapat menggunakanobj.GetType().GetProperty("Name")?.GetSetMethod()?.Invoke(...)
tecfield
1
tidak mungkin untuk menetapkan nilai untuk CanWrite=Falsetipe, kan?
T.Todua
287

Anda juga dapat melakukan:

Type type = target.GetType();

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

prop.SetValue (target, propertyValue, null);

di mana target adalah objek yang akan menetapkan propertinya.

El Cheicon
sumber
11
Saya telah melakukan hal yang sama hari ini. Di atas berfungsi dengan baik, jelas pemeriksaan nol harus dilakukan pada prop sebelum mencoba menggunakannya.
Antony Scott
3
@AntonyScott Saya pikir Anda ingin tahu jika Anda menggunakan properti yang salah, jadi "gagal diam-diam" sepertinya jalan yang salah.
jih
3
@ Aku bisa mengerti maksudmu, tapi itu tergantung situasi sebenarnya.
Antony Scott
94

Refleksi, pada dasarnya, yaitu

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

atau ada perpustakaan untuk membantu dalam hal kenyamanan dan kinerja; misalnya dengan FastMember :

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

(yang juga memiliki keuntungan karena tidak perlu tahu sebelumnya apakah itu bidang vs properti)

Marc Gravell
sumber
Wow, agak bingung dari penggabungan, tetapi saya menemukan jawaban Anda lagi! Terima kasih, Anda berhak menerima 'terima', tetapi karena utas saya digabung :( Terima kasih lagi!
halfpastfour.am
@MarcGravell, saya melihat FastMember dan ini cukup menarik. Apakah ada permulaan / tutorial di suatu tempat bagi kita manusia biasa untuk menggunakan lib besar Anda?
Sudhanshu Mishra
Bagaimana saya bisa mendapatkan tipe properti oleh FastMember?
Said Roohullah Allem
@Jahan accessor => GetMembers => Anggota => Ketik
Marc Gravell
jawaban bagus! Hal yang baik tentang oneliner adalah bahwa hal itu jauh lebih cepat untuk dipahami, karena tidak ada variabel bernama pengguna di antaranya yang untuk Anda sendiri mungkin tidak masuk akal ..
Bastiaan
27

Atau Anda bisa membungkus satu liner Marc di dalam kelas ekstensi Anda sendiri:

public static class PropertyExtension{       

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

dan menyebutnya seperti ini:

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

Untuk ukuran yang baik, mari tambahkan metode untuk mendapatkan nilai properti:

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

Ya, menggunakan System.Reflection:

using System.Reflection;

...

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

Gunakan sesuatu seperti ini:

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

atau

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);
   }
}
Ardalan Shahgholi
sumber
2
Bagian di mana Anda mendapatkan jenis properti dan melemparkannya benar-benar berguna bagi saya. Itu bekerja seperti pesona. Terima kasih
Marc
12

Anda juga dapat mengakses bidang menggunakan cara yang sama:

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

Dengan refleksi semuanya bisa menjadi buku terbuka :) Dalam contoh saya, kami mengikat bidang tingkat instance pribadi.

JoshBerke
sumber
8

Anda dapat mencoba ini ketika Anda ingin menetapkan properti Obyek secara massal dari Objek lain menggunakan nama Properti:

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;
            }
        }
    }
}
pengguna3679106
sumber
2
Hai dan selamat datang di Stack Overflow. Harap format kode Anda dengan benar dan tidak hanya membuangnya di posting Anda, itu akan membantu orang lain memahami jawaban Anda.
ThreeFx
0

Saya baru saja menerbitkan paket Nuget yang memungkinkan pengaturan tidak hanya Properti tingkat pertama tetapi juga properti bersarang di objek yang diberikan secara mendalam.

Ini paketnya

Menetapkan nilai properti suatu objek berdasarkan pathnya dari root.

Objek dapat berupa objek kompleks dan properti dapat berupa properti bersarang multi level atau dapat berupa properti langsung di bawah root. ObjectWriterakan menemukan properti menggunakan parameter jalur properti dan memperbarui nilainya. Path properti adalah nama tambahan dari properti yang dikunjungi dari root ke properti simpul akhir yang ingin kita atur, dibatasi oleh parameter string pembatas.

Pemakaian:

Untuk mengatur properti langsung di bawah root objek:

Yaitu. LineItemkelas memiliki properti int yang disebutItemId

LineItem lineItem = new LineItem();

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

Untuk menyiapkan properti bersarang beberapa level di bawah root objek:

Yaitu. Invitekelas memiliki properti yang disebut State, yang memiliki properti yang disebut Invite(tipe Undang), yang memiliki properti yang disebut Recipient, yang memiliki properti yang disebutId .

Untuk membuat segala sesuatunya lebih kompleks, Stateproperti itu bukan tipe referensi, itu adalah struct.

Di sini adalah bagaimana Anda dapat mengatur properti Id (ke nilai string "pandangan") di bagian bawah pohon objek dalam satu baris.

Invite invite = new Invite();

ObjectWriter.Set(invite, "State_Invite_Recipient_Id", "outlook", delimiter: "_");
Dogu Arslan
sumber
0

Berdasarkan saran MarcGravell, saya telah membangun metode statis berikut. Metode ini secara umum menetapkan semua properti yang cocok dari objek sumber ke target menggunakan 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);
            }
        }
    }
Kuat
sumber