2009-04-06 16 views
49

No estoy seguro de si esto es posible, pero quiero iterar a través de una clase y establecer una propiedad de miembro campo sin hacer referencia al objeto de campo de forma explícita:C# - la manera de recorrer los campos de clases y establecer las propiedades

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

en la respuesta que he marcado como la respuesta que tenga que añadir:

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

Esto falla porque intentó establecer el nombre de la propiedad en el objeto memberinfo. La información del miembro tiene un nombre, pero no es el nombre de [_person]. –

Respuesta

27
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); 
    } 
} 
+0

Necesitaba agregar var p = person.GetType() ... como terminé llamándolo desde una clase diferente – Petras

+2

Esto no funciona. Debería haber sido 'GetProperty' en lugar de' GetField'. – c00000fd

4

Está tratando de establecer la propiedad Name del campo _person de la clase de su Employee. No tiene uno. Prueba esto:

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

No estoy seguro de si es necesario emitir el primer argumento de esta manera:

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

Esto entonces se aplica al valor del campo _person lugar.

+0

Aún recibo el mismo error, al menos en mi computadora. – Samuel

+0

¿Posiblemente necesita lanzar el primer argumento? Editará la publicación. –

+0

Yo también, es el mismo error – Petras

4

Intenta realizar SetValue() en la propiedad Name de la variable member que es un objeto MemberInfo y esta propiedad es de solo lectura.

Tenga en cuenta que no necesita iterar sobre todos los miembros y no necesita obtener el campo _person con reflexión, ya que se define en la misma clase que el método DynamicallySetPersonProperty().

Así que el código debería decir así.

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

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

La primera línea fallará si _person es nulo. Entonces puede usar reflectiopn para obtener el tipo de campo.

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

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

Pero ahora acceso a esta característica será todavía fallar si _person es nulo.

property.Setvalue(field.GetValue(this), "new name", null); 
30

Aquí está un ejemplo de trabajo completo:

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: añadido una variante de bucle por lo que podría ver la forma de bucle a través de objetos de información de la propiedad.

+1

Esto debería haber sido marcado como la respuesta. – c00000fd

10

Con los siguientes métodos de extensión que he creado, se puede establecer u obtener cualquier valor de la propiedad, incluso si se anidan

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

o conjunto

SetPropertyValue (customObject, "Property.Nested.Child.. Nombre", "mi nombre de encargo");

 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 (!string.IsNullOrEmpty(propertyName)) 
      { 
       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); 
     } 
    } 

Y aquí hay un par de pruebas para ello

[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"); 

    } 
} 

Espero que les sea útil

+0

Excelente, esto funciona – Petras

+0

Si esto funciona, por favor agréguelo como una respuesta correcta: D – Paleta

+0

Muy bonito ... especialmente con la anidación. El ejemplo de Plinth es más simple, pero no tiene anidamiento. Además, para cualquier persona que aterrice aquí que (como yo) tuvo que investigar para entender qué diablos eran las "extensiones", esta es una explicación buena y concisa: http://msdn.microsoft.com/en-us/library/ bb311042.aspx – Russ

1

intente esto:

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

ejemplo de uso:

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

    } 
Cuestiones relacionadas