views:

6744

answers:

7

I am not sure if this is possible but I want to iterate through a class and set a field member property without referring to the field object explicitly:

public class Employee
{
  public Person _person = new Person();

  public void DynamicallySetPersonProperty()
  {
    MemberInfo[] members = this.GetType().GetMembers();

    foreach (MemberInfo member in members.Where(a => a.Name == "_person"))
    //get the _person field
    {

      Type type = member.GetType();
      PropertyInfo prop = type.GetProperty("Name"); //good, this works, now to set a value for it

      //this line does not work - the error is "property set method not found"
      prop.SetValue(member, "new name", null);
    }
  }
}


public class Person
{
  public string Name { get; set; }
}

In the answer that I marked as the answer you need to add:

  public static bool IsNullOrEmpty(this string source)
  {
    return (source == null || source.Length > 0) ? true : false;
  }

THANKS

Thanks, some excellent answers below that got it working for me.

A: 

Have a look on this CodeProject article related to what you are trying to do

http://www.codeproject.com/KB/cs/fast_dynamic_properties.aspx

Chris Ballance
+3  A: 

You are trying to set the Name property of your Employee class's _person field. It doesn't have one. Try this:

prop.SetValue(((FieldInfo)member).GetValue(this), "new name", null)

Not sure if you need to cast the first argument like this:

prop.SetValue((Person)((FieldInfo)member).GetValue(this), "new name", null)

This then applies it to the value of the _person field instead.

David M
Still get the same error, at least on my computer.
Samuel
Possibly need to cast the first argument? Will edit post.
David M
So do I, its the same error
Petras
+3  A: 

You a trying to perform SetValue() on the property Name of the variable member that is a MemberInfo object and this proeprty is read only.

Note you do not need to iterate over all memebers and you do not need to get the field _person with reflection as it is defined in the same class as the method DynamicallySetPersonProperty().

So the code shoul read like this.

PropertyInfo property = this._person.GetType().GetProperty("Name");

property.SetValue(this._person, "new name", null);

The first line will fail if _person is null. So you can use reflectiopn to get the type of the field.

FieldInfo field = this.GetType().GetField("_person", BindingFlags.Public);

PropertyInfo property = field.FieldType.GetProperty("Name");

But now accessing this property will still fail if _personis null.

property.Setvalue(field.GetValue(this), "new name", null);
Daniel Brückner
+3  A: 
public class Person
{
    public string Name { get; set; }
}

public class Employee
{
    public Person person = new Person();

    public void DynamicallySetPersonProperty()
    {
        var p = GetType().GetField("person").GetValue(this);
        p.GetType().GetProperty("Name").SetValue(p, "new name", null);
    }
}
Koistya Navin
I needed to add var p = person.GetType()... as I ended up calling it from a different class
Petras
+1  A: 

Here's a complete working example:

public class Person
{
    public string Name { get; set; }
}

class Program
{
    static void PropertySet(object p, string propName, object value)
    {
        Type t = p.GetType();
        PropertyInfo info = t.GetProperty(propName);
        if (info == null)
            return;
        if (!info.CanWrite)
            return;
        info.SetValue(p, value, null);
    }

    static void PropertySetLooping(object p, string propName, object value)
    {
        Type t = p.GetType();
        foreach (PropertyInfo info in t.GetProperties())
        {
            if (info.Name == propName && info.CanWrite)
            {
                info.SetValue(p, value, null);
            }
        }
    }

    static void Main(string[] args)
    {
        Person p = new Person();

        PropertySet(p, "Name", "Michael Ellis");
        Console.WriteLine(p.Name);
        PropertySetLooping(p, "Name", "Nigel Mellish");
        Console.WriteLine(p.Name);
    }
}

EDIT: added a looping variant so you could see how to loop through property info objects.

plinth
+3  A: 

With the following Extension methods that I have created, you can set or get any property value even if they are nested

GetPropertyValue(customObject, "Property.Nested.Child.Name");

or set

SetPropertyValue(customObject, "Property.Nested.Child.Name", "my custom name");

     private class TargetProperty
 {
  public object Target { get; set; }
  public PropertyInfo Property { get; set; }

  public bool IsValid { get { return Target != null && Property != null; } }
 }

 private static TargetProperty GetTargetProperty(object source, string propertyName)
 {
  if (!propertyName.Contains("."))
   return new TargetProperty { Target = source, Property = source.GetType().GetProperty(propertyName) };

  string[] propertyPath = propertyName.Split('.');

  var targetProperty = new TargetProperty();

  targetProperty.Target = source;
  targetProperty.Property = source.GetType().GetProperty(propertyPath[0]);

  for (int propertyIndex = 1; propertyIndex < propertyPath.Length; propertyIndex++)
  {
   propertyName = propertyPath[propertyIndex];
   if (!propertyName.IsNullOrEmpty())
   {
    targetProperty.Target = targetProperty.Property.GetValue(targetProperty.Target, null);
    targetProperty.Property = targetProperty.Target.GetType().GetProperty(propertyName);
   }
  }

  return targetProperty;
 }


 public static bool HasProperty(this object source, string propertyName)
 {
  return GetTargetProperty(source, propertyName).Property != null;
 }

 public static object GetPropertyValue(this object source, string propertyName)
 {
  var targetProperty = GetTargetProperty(source, propertyName);
  if (targetProperty.IsValid)
  {
   return targetProperty.Property.GetValue(targetProperty.Target, null);
  }
  return null;
 }

 public static void SetPropertyValue(this object source, string propertyName, object value)
 {
  var targetProperty = GetTargetProperty(source, propertyName);
  if(targetProperty.IsValid)
  {
   targetProperty.Property.SetValue(targetProperty.Target, value, null);
  }
 }

And here are a couple of tests for it

    [TestFixture]
public class ObjectExtensionsTest
{

 private class MockClass
 {
  public MockClass()
  {
   Nested = new NestedMockClass();
  }

  public string Id { get; set; }
  public string Name { get; set; }

  public string GetOnly { get { return "MockClass"; } }
  public string SetOnly { set { } }

  public NestedMockClass Nested { get; set; }
 }

 private class NestedMockClass
 {
  public string NestedId { get; set; }
  public string NestedName { get; set; }

  public string NestedGetOnly { get { return "NestedMockClass"; } }
  public string NestedSetOnly { set { } }
 }

 [Test]
 public void TestShouldFindProperty()
 {
  MockClass mockObject = new MockClass();

  Assert.IsTrue(mockObject.HasProperty("Id"));
  Assert.IsTrue(mockObject.HasProperty("Name"));
  Assert.IsTrue(mockObject.HasProperty("GetOnly"));
  Assert.IsTrue(mockObject.HasProperty("SetOnly"));
  Assert.IsTrue(mockObject.HasProperty("Nested"));
  Assert.IsTrue(mockObject.HasProperty("Nested.NestedId"));
  Assert.IsTrue(mockObject.HasProperty("Nested.NestedName"));
  Assert.IsTrue(mockObject.HasProperty("Nested.NestedGetOnly"));
  Assert.IsTrue(mockObject.HasProperty("Nested.NestedSetOnly"));
 }

 [Test]
 public void TestShouldGetPropertyValue()
 {
  MockClass mockObject = new MockClass();

  mockObject.Id = "1";
  mockObject.Name = "Name";
  mockObject.Nested.NestedId = "NestedId";
  mockObject.Nested.NestedName = "NestedName";

  Assert.AreEqual(mockObject.Id, mockObject.GetPropertyValue("Id"));
  Assert.AreEqual(mockObject.Name, mockObject.GetPropertyValue("Name"));
  Assert.AreEqual(mockObject.GetOnly, mockObject.GetPropertyValue("GetOnly"));
  Assert.AreEqual(mockObject.Nested.NestedId, mockObject.GetPropertyValue("Nested.NestedId"));
  Assert.AreEqual(mockObject.Nested.NestedName, mockObject.GetPropertyValue("Nested.NestedName"));

 }

 [Test]
 public void TestShouldSetPropertyValue()
 {
  MockClass mockObject = new MockClass();

  mockObject.SetPropertyValue("Id", "1");
  mockObject.SetPropertyValue("Name", "Name");
  mockObject.SetPropertyValue("Nested.NestedId", "NestedId");
  mockObject.SetPropertyValue("Nested.NestedName", "NestedName");

  Assert.AreEqual(mockObject.Id, "1");
  Assert.AreEqual(mockObject.Name, "Name");
  Assert.AreEqual(mockObject.Nested.NestedId, "NestedId");
  Assert.AreEqual(mockObject.Nested.NestedName, "NestedName");

 }
}

Hope you find it useful.

Paleta
Excellent, this works
Petras
If this works, please add it as a correct answer :D
Paleta
A: 

public static void ApplyPropertyChanges(this object objDest, object objToCopyFrom) { if (objDest == null) throw new ArgumentNullException(); if (objToCopyFrom == null) throw new ArgumentNullException("objToCopyFrom"); if (objDest.GetType() != objToCopyFrom.GetType()) throw new Exception("Invalid type. Required: \"" + objDest.GetType().ToString() + "\"");

        foreach (System.Reflection.PropertyInfo piOrig in objDest.GetType().GetProperties())
        {
            object editedVal = objToCopyFrom.GetType().GetProperty(piOrig.Name).GetValue(objToCopyFrom, null);

            piOrig.SetValue(objDest,
            editedVal,
            null);
        }
    }

usage example:

    public ActionResult Edit(Team editedTeamData)
    {
        if (!ModelState.IsValid)
            return View();

        Team origTeam = (from t in _db.Teams
                         where t.TeamID == editedTeamData.TeamID
                         select t).FirstOrDefault();

        origTeam.ApplyPropertyChanges(editedTeamData);
        _db.SubmitChanges();

        return RedirectToAction("Index");

    }