ChatGPT解决这个技术问题 Extra ChatGPT

Set object property using reflection

Is there a way in C# where I can use reflection to set an object property?

Ex:

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

I want to set obj.Name with reflection. Something like:

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

Is there a way of doing this?


j
jpaugh

Yes, you can use Type.InvokeMember():

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

This will throw an exception if obj doesn't have a property called Name, or it can't be set.

Another approach is to get the metadata for the property, and then set it. This will allow you to check for the existence of the property, and verify that it can be set:

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

If you aren't dealing with all strings you might wanna convert the data first: var val = Convert.ChangeType(propValue, propInfo.PropertyType); source: devx.com/vb2themax/Tip/19599
alternatively, you can use obj.GetType().GetProperty("Name")?.GetSetMethod()?.Invoke(...)
its impossible to set value for CanWrite=False types, right?
E
El Cheicon

You can also do:

Type type = target.GetType();

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

prop.SetValue (target, propertyValue, null);

where target is the object that will have its property set.


I have done this very same thing today. The above works great, obviously a null check should be done on prop before attempting to use it.
@AntonyScott I would think you'd want to know if you're invoking the wrong property, so "fail silently" seems like a bad course.
@j.i.h. I can see your point, but it depends on the situation really.
M
Marc Gravell

Reflection, basically, i.e.

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

or there are libraries to help both in terms of convenience and performance; for example with FastMember:

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

(which also has the advantage of not needing to know in advance whether it is a field vs a property)


Wow, got a little confused from the merge, but i found your answer again! Thank you, you deserve an 'accept', but since my thread is merged :( Thanks again!
@MarcGravell, I was looking at FastMember and it is pretty interesting. Is there a getting started/tutorial somewhere for us mere mortals to use this great lib of yours?
How can I get the type of property by FastMember?
@Jahan accessor => GetMembers => Member => Type
great answer! The good thing about a oneliner is that it is much faster to understand, since there are no user named variables in between which for yourself might not make any sense..
E
Erik K.

Or you could wrap Marc's one liner inside your own extension class:

public static class PropertyExtension{       

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

and call it like this:

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

For good measure, let's add a method to get a property value:

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

b
bluish

Yes, using System.Reflection:

using System.Reflection;

...

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

W
Wai Ha Lee

Use somethings like this :

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

or

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

The part where you get the property type and then cast it was really useful for me. It works like a charm. Thank you
J
JoshBerke

You can also access fields using a simillar manner:

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

With reflection everything can be an open book:) In my example we are binding to a private instance level field.


4
4444

You can try this out when you want to mass-assign properties of an Object from another Object using Property names:

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

Hi and welcome to Stack Overflow. Please format your code properly and not just dump it in your post, it will help others understand your answer.
M
Mong Zhu

I have just published a Nuget package that allows setting up not only the first level Properties but also nested properties in the given object in any depth.

Here is the package

Sets the value of a property of an object by its path from the root.

The object can be a complex object and the property can be multi level deep nested property or it can be a property directly under the root. ObjectWriter will find the property using the property path parameter and update its value. Property path is the appended names of the properties visited from root to the end node property which we want to set, delimited by the delimiter string parameter.

Usage:

For setting up the properties directly under the object root:

Ie. LineItem class has an int property called ItemId

LineItem lineItem = new LineItem();

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

For setting up nested property multiple levels below the object root:

Ie. Invite class has a property called State, which has a property called Invite (of Invite type), which has a property called Recipient, which has a property called Id.

To make things even more complex, the State property is not a reference type, it is a struct.

Here is how you can set the Id property (to string value of “outlook”) at the bottom of the object tree in a single line.

Invite invite = new Invite();

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

C
Cogent

Based on MarcGravell's suggestion, I have constructed the following static method.The method generically assigns all matching properties from source object to target using 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);
            }
        }
    }