2009-04-06 9 views
49

Je ne sais pas si cela est possible, mais je veux itérer une classe et définir une propriété de membre sur le terrain sans se référer à l'objet champ explicitement:C# - Comment parcourir les classes champs et définir les propriétés

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

Dans la réponse que j'ai marqué que la réponse vous devez ajouter:

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

Cela échoue car vous avez essayé de définir le nom de la propriété sur l'objet memberinfo. Info membre a un nom, mais ce n'est pas le nom de [_personne]. –

Répondre

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

Je devais ajouter var p = person.GetType() ... comme j'ai fini par l'appeler d'une classe différente – Petras

+2

Cela ne fonctionne pas. Cela aurait dû être 'GetProperty' au lieu de' GetField'. – c00000fd

4

Vous essayez de définir la propriété Name du champ _person de votre classe Employee. Il n'en a pas. Essayez ceci:

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

Je ne sais pas si vous avez besoin de jeter le premier argument comme celui-ci:

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

Cette applique ensuite à la valeur du champ _person à la place.

+0

Toujours obtenir la même erreur, au moins sur mon ordinateur. – Samuel

+0

Peut-être besoin de lancer le premier argument? Éditera poste. –

+0

Moi aussi, c'est la même erreur – Petras

4

Vous essayez d'exécuter SetValue() sur la propriété Name de la variable member qui est un objet MemberInfo et ce proeprty est en lecture seule.

Notez que vous n'avez pas besoin de parcourir tous les membres et vous n'avez pas besoin d'obtenir le champ _person avec réflexion car il est défini dans la même classe que la méthode DynamicallySetPersonProperty().

Donc le code doit être lu comme ceci.

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

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

La première ligne échouera si _person est null. Vous pouvez donc utiliser reflectiopn pour obtenir le type du champ.

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

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

Mais maintenant accéder à cette propriété encore échouer si _person est nulle.

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

Voici un exemple de travail complet:

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: Ajout d'une variante en boucle pour que vous puissiez voir comment faire une boucle à travers des objets d'information de la propriété.

+1

Cela aurait dû être marqué comme la réponse. – c00000fd

10

Avec les méthodes d'extension suivantes que j'ai créé, vous pouvez définir ou d'obtenir une valeur de la propriété, même si elles sont imbriquées

GetPropertyValue (customObject, « Property.Nested.Child.Name »);

ou un ensemble

SetPropertyValue (customObject, « Property.Nested.Child.. Nom », "mon nom personnalisé");

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

Et voici quelques tests pour elle

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

    } 
} 

Espoir vous trouvez utile

+0

Excellent, cela fonctionne – Petras

+0

Si cela fonctionne, s'il vous plaît ajouter comme une réponse correcte: D – Paleta

+0

Très gentil ... surtout avec nidification. L'exemple de Plinth est plus simple, mais n'a pas d'imbrication. Aussi, pour toute personne qui atterrissait ici et qui (comme moi) devait creuser pour comprendre ce que sont les "extensions", voici une bonne explication concise: http://msdn.microsoft.com/fr-fr/library/ bb311042.aspx – Russ

1

essayez ceci:

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

exemple d'utilisation:

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

    } 
Questions connexes