¿Es posible en C # acceder a los campos de un objeto utilizando los nombres de campo generados en tiempo de ejecución

StackOverflow https://stackoverflow.com/questions/1647415

  •  22-07-2019
  •  | 
  •  

Pregunta

Esto es lo que quiero decir:

Necesito poder sustituir este código C # de aspecto feo:

if (attribute.Name == "Name") machinePool.Name = attribute.Value;
else if (attribute.Name == "Capabilities") machinePool.Capabilities = attribute.Value;
else if (attribute.Name == "FillFactor") machinePool.FillFactor = attribute.Value;

en algo como esto:

machinePool.ConvertStringToObjectField(attribute.Name) = attribute.Value;

No hay un método ConvertStringToObjectField (), pero me gustaría tener algo como esto, si es posible. Tengo acceso al código de clase de objeto machinePool, por lo que puedo agregar el código necesario, pero no estoy seguro de qué código puede ser o si es posible hacerlo en C #.

¿Fue útil?

Solución

Sí, puedes hacerlo a través de la reflexión:

var fieldInfo = machinePool.GetType().GetField(attribute.Name);
fieldInfo.SetValue(machinePool, attribute.Value);

También puede crear un método de extensión para facilitar las cosas:

public static void SetField(this object o, string fieldName, object value)
{
    var fi = o.GetType().GetField(fieldName);
    fi.SetValue(o, value);
}

Otros consejos

Sí, puedes. Aquí hay un código que uso cuando quiero obtener nombres de propiedad como cadenas (es decir, para manejar un PropertyChangedEventHandler) pero no quiero usar las cadenas en sí mismas:

    public static string GetPropertyName<T>(Expression<Func<T, object>> propertyExpression)
    {
        Check.RequireNotNull<object>(propertyExpression, "propertyExpression");
        switch (propertyExpression.Body.NodeType)
        {
            case ExpressionType.MemberAccess:
                return (propertyExpression.Body as MemberExpression).Member.Name;
            case ExpressionType.Convert:
                return ((propertyExpression.Body as UnaryExpression).Operand as MemberExpression).Member.Name;
        }
        var msg = string.Format("Expression NodeType: '{0}' does not refer to a property and is therefore not supported", 
            propertyExpression.Body.NodeType);
        Check.Require(false, msg);
        throw new InvalidOperationException(msg);
    }

Y aquí hay un código de prueba para ayudarlo a resolverlo:

[TestFixture]
public class ExpressionsExTests
{
    class NumbNut
    {
        public const string Name = "blah";
        public static bool Surname { get { return false; } }
        public string Lame;
        public readonly List<object> SerendipityCollection = new List<object>();
        public static int Age { get { return 12; }}
        public static bool IsMammel { get { return _isMammal; } }
        private const bool _isMammal = true;
        internal static string BiteMe() { return "bitten"; }
    }

    [Test]
    public void NodeTypeIs_Convert_aka_UnaryExpression_Ok()
    {
        Assert.That(ExpressionsEx.GetPropertyName<NumbNut>(nn => NumbNut.Age), Is.EqualTo("Age"));
        Assert.That(ExpressionsEx.GetPropertyName<NumbNut>(nn => NumbNut.IsMammel), Is.EqualTo("IsMammel"));
        Assert.That(ExpressionsEx.GetPropertyName<NumbNut>(nn => NumbNut.Surname), Is.EqualTo("Surname"));
    }

    [Test]
    public void NodeTypeIs_MemberAccess_aka_MemberExpression_Ok()
    {
        Assert.That(ExpressionsEx.GetPropertyName<NumbNut>(nn => nn.SerendipityCollection), Is.EqualTo("SerendipityCollection"));
        Assert.That(ExpressionsEx.GetPropertyName<NumbNut>(nn => nn.Lame), Is.EqualTo("Lame"));
    }

    [Test]
    public void NodeTypeIs_Call_Error()
    {
        CommonAssertions.PreconditionCheck(() => ExpressionsEx.GetPropertyName<NumbNut>(nn => NumbNut.BiteMe()),
                                           "does not refer to a property and is therefore not supported");
    }

    [Test]
    public void NodeTypeIs_Constant_Error() {
        CommonAssertions.PreconditionCheck(() => ExpressionsEx.GetPropertyName<NumbNut>(nn => NumbNut.Name),
                                           "does not refer to a property and is therefore not supported");
    }

    [Test]
    public void IfExpressionIsNull_Error()
    {
        CommonAssertions.NotNullRequired(() => ExpressionsEx.GetPropertyName<NumbNut>(null));
    }

    [Test]
    public void WasPropertyChanged_IfPassedNameIsSameAsNameOfPassedExpressionMember_True()
    {
        Assert.That(ExpressionsEx.WasPropertyChanged<NumbNut>("SerendipityCollection", nn => nn.SerendipityCollection), Is.True);
    }

    [Test]
    public void WasPropertyChanged_IfPassedPropertyChangeArgNameIsSameAsNameOfPassedExpressionMember_True()
    {
        var args = new PropertyChangedEventArgs("SerendipityCollection");
        Assert.That(ExpressionsEx.WasPropertyChanged<NumbNut>(args, nn => nn.SerendipityCollection), Is.True);
    }

}

HTH

Berryl

Podrías hacer algo como:

void SetPropertyToValue(string propertyName, object value)
{
    Type type = this.GetType();
    type.GetProperty(propertyName).SetValue(this, value, null);
}

Luego úsalo como:

machinePool.SetPropertyToValue(attribute.Name, attribute.Value);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top