50 votes

c# - Comment itérer dans les champs d'une classe et définir les propriétés ?

Je ne sais pas si c'est possible mais je veux itérer dans une classe et définir la propriété d'un membre de champ sans faire référence à l'objet de champ explicitement :

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ée comme étant la réponse que vous devez ajouter :

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

0 votes

Cela échoue parce que vous avez essayé de définir le nom de la propriété sur l'objet memberinfo. Member Info a un nom, mais ce n'est pas le nom du [_person].

34voto

plinth Points 26817

Voici un exemple complet de fonctionnement :

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 de bouclage pour que vous puissiez voir comment boucler les objets d'information sur les propriétés.

29voto

Konstantin Tarkus Points 16862
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 votes

J'ai dû ajouter var p = person.GetType()... car j'ai fini par l'appeler depuis une classe différente

3 votes

Ça ne marche pas. Ça aurait dû être GetProperty au lieu de GetField .

10voto

Paleta Points 310

Avec les méthodes Extension suivantes que j'ai créées, vous pouvez définir ou obtenir n'importe quelle valeur de propriété, même si elles sont imbriquées.

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

ou mettre

SetPropertyValue(customObject, "Property.Nested.Child.Name", "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 cela

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

    }
}

J'espère que vous le trouverez utile.

0 votes

Très bien... surtout avec l'imbrication. L'exemple de Plinth est plus simple, mais n'a pas d'imbrication. De plus, pour tous ceux qui ont atterri ici et qui (comme moi) ont dû creuser pour comprendre ce qu'étaient les "extensions", voici une bonne et concise explication : msdn.microsoft.com/fr/us/library/bb311042.aspx

5voto

David M Points 45808

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 suis pas sûr que vous ayez besoin de présenter le premier argument comme ça :

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

Il s'applique alors à la valeur du champ _person à la place.

4voto

Daniel Brückner Points 36242

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

Notez que vous n'avez pas besoin d'itérer sur tous les membres et que vous n'avez pas besoin de récupérer le champ _person avec réflexion car elle est définie dans la même classe que la méthode DynamicallySetPersonProperty() .

Le code devrait donc se lire comme suit.

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

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

La première ligne échouera si _person est nulle. Vous pouvez donc utiliser la réflectiopn pour obtenir le type du champ.

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

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

Mais maintenant l'accès à cette propriété échouera toujours si _person est nulle.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X