2012-10-01 33 views
19

Me disculpo si esto está cubierto en alguna parte. Investigué antes de publicar!cómo obtener campos y propiedades en una sola llamada mediante la reflexión?

bien, entonces pregunta ... Estoy usando GetType() .GetProperties, pero no devuelve campos de instancia simples, que no tienen get/set en ellos ... así que usé .GetFields, que los encuentra, pero quiero obtener un único objeto simple para obtener/establecer un valor sin voltear entre campos y propiedades ... ¿es esto posible?

mi código actual funciona en PropertyInfo, que funciona muy bien, pero eso no es para campos, supongo?

[editar] esta es la solución que se me ocurrió, que funciona bien. gracias a todos ....

// some logic borrowed from James Newton-King, http://www.newtonsoft.com 
    public static void SetValue(this MemberInfo member, object property, object value) 
    { 
     if (member.MemberType == MemberTypes.Property) 
      ((PropertyInfo)member).SetValue(property, value, null); 
     else if (member.MemberType == MemberTypes.Field) 
      ((FieldInfo)member).SetValue(property, value); 
     else 
      throw new Exception("Property must be of type FieldInfo or PropertyInfo"); 
    } 

    public static object GetValue(this MemberInfo member, object property) 
    { 
     if (member.MemberType == MemberTypes.Property) 
      return ((PropertyInfo)member).GetValue(property, null); 
     else if (member.MemberType == MemberTypes.Field) 
      return ((FieldInfo)member).GetValue(property); 
     else 
      throw new Exception("Property must be of type FieldInfo or PropertyInfo"); 
    } 

    public static Type GetType(this MemberInfo member) 
    { 
     switch (member.MemberType) 
     { 
      case MemberTypes.Field: 
       return ((FieldInfo)member).FieldType; 
      case MemberTypes.Property: 
       return ((PropertyInfo)member).PropertyType; 
      case MemberTypes.Event: 
       return ((EventInfo)member).EventHandlerType; 
      default: 
       throw new ArgumentException("MemberInfo must be if type FieldInfo, PropertyInfo or EventInfo", "member"); 
     } 
    } 

Respuesta

22

¿Qué tal:

const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance; 
MemberInfo[] members = type.GetFields(bindingFlags).Cast<MemberInfo>() 
    .Concat(type.GetProperties(bindingFlags)).ToArray(); 

Alternativamente, las bibliotecas como FastMember trabajarán felizmente con cualquiera de los campos o propiedades, con get/set idénticas independientemente del tipo de miembro.

+0

Desde OP: "obtener un objeto simple simple para obtener/establecer un valor". No estoy seguro de cómo MemberInfo hace eso. – CrazyCasta

+1

@CrazyCasta de hecho; si lo desea, debe salir de la API de reflexión central ya que * no hay * interfaz compartida única para eso. Sin embargo, existen bibliotecas separadas, por lo que cité FastMember. –

+1

Realmente me gustaría marcar tanto @CrazyCasta como orc como respuestas, ya que son útiles, pero al final fui con el MemberInfo utilizando métodos de extensión. – Timmerz

10

El regreso tipos de GetProperties() y (GetFields) son diferentes, como parece que han notado. Debería definir una interfaz con GetValue() y SetValue() y usar extend ParameterInfo y FieldInfo para implementar esta interfaz. Esto probablemente funcionaría como un envoltorio:

interface IGetSettable 
{ 
    public void SetValue(
     Object obj, 
     Object value, 
     Object[] index); 
    public Object GetValue(
     Object obj, 
     Object[] index); 
} 

public class ParameterInfoGS : IGetSettable 
{ 
    protected ParameterInfo pi; 

    public ParameterInfoExtra(ParameterInfo _pi) 
    { 
     pi = _pi; 
    } 

    public void SetValue(
     Object obj, 
     Object value, 
     Object[] index) {pi.SetValue(obj, value, index);} 
    public Object GetValue(
     Object obj, 
     Object[] index) {return pi.GetValue(obj, index);} 
} 

public class FieldInfoGS : IGetSettable 
{ 
    protected FieldInfo pi; 

    public FieldInfoExtra(FieldInfo _pi) 
    { 
     pi = _pi; 
    } 

    public void SetValue(
     Object obj, 
     Object value, 
     Object[] index) {pi.SetValue(obj, value, index);} 
    public Object GetValue(
     Object obj, 
     Object[] index) {return pi.GetValue(obj, index);} 
} 

public static class AssemblyExtension 
{ 
    public static IGetSettable[] GetParametersAndFields(this Type t) 
    { 
     List<IGetSettable> retList = new List<IGetSettable>(); 

     foreach(ParameterInfo pi in t.GetParameters()) 
      retList.Add(new ParameterInfoExtra(pi)); 

     foreach(FieldInfo fi in t.GetFields()) 
      retList.Add(new FieldInfoExtra(fi)); 

     return retList.ToArray(); 
    } 
} 

Esto le permitirá hacer GetType().GetParametersAndFields() (es decir, utilizar los tipos de reflexión estándar).

+3

'MemberInfo'; p –

+4

@MarcGravell Eso no tiene los métodos get y set. – CrazyCasta

2

Usando DLR (bastante simple, si se conoce el nombre de usuario en tiempo de compilación):

((dynamic)obj).MyFieldOrPropertyName = myValue; 

Si sólo conoce el nombre del miembro en tiempo de ejecución , me gustaría recomendar FastMember, como Marc Gravell sugirió.

6

Un poco tarde, pero se me ocurrió lo siguiente ... 1 bucle, funciona como un encanto ;-)

 MemberInfo[] memberInfos = dotNetType.GetMembers(); 
     ModelPropertySpec modelPropertySpec; 
     foreach (MemberInfo memberInfo in memberInfos) 
     { 
      Type itemType = null; 
      String memberName = memberInfo.Name; 
      switch (memberInfo.MemberType) 
      { 
       case MemberTypes.Property: 
        itemType = dotNetType.GetProperty(memberName).PropertyType; 
        break; 
       case MemberTypes.Field: 
        itemType = dotNetType.GetField(memberName).FieldType; 
        break; 
      } 

      if (itemType != null) 
      { 
       modelPropertySpec = ParsePropertyType(memberName, itemType); 
       modelSpec.Properties.Add(modelPropertySpec.Name, modelPropertySpec); 
      } 
     } 
5

Para obtener cualquiera de las propiedades o campos, se puede decir:

var q= 
    from it in type.GetMembers(bindingAttr) 
    where it is PropertyInfo||it is FieldInfo 
    select it; 

donde bindingAttr podría ser

var bindingAttr= 
     BindingFlags.NonPublic| 
     BindingFlags.Public| 
     BindingFlags.Instance; 

Retire BindingFlags.NonPublic si no desea conseguir no pu miembros blic. Por cierto, la consulta no es una sola llamada sino una sola declaración .


para obtener el valor de cualquiera de una propiedad o un campo sin poner por sí mismo, utilice InvokeMember para el truco:

static object GetValue<T>(
     T x, object target) where T:MemberInfo { 
    var invokeAttr=(
      x is FieldInfo 
       ?BindingFlags.GetField 
       :x is PropertyInfo 
        ?BindingFlags.GetProperty 
        :BindingFlags.Default)| 
      BindingFlags.NonPublic| 
      BindingFlags.Public| 
      BindingFlags.Instance; 

    return target.GetType().InvokeMember(
     x.Name, invokeAttr, default(Binder), target, null); 
} 

similar, para establecer el valor:

static void SetValue<T>(
     T x, object target, object value) where T:MemberInfo { 
    var args=new object[] { value }; 
    var invokeAttr=(
      x is FieldInfo 
       ?BindingFlags.SetField 
       :x is PropertyInfo 
        ?BindingFlags.SetProperty 
        :BindingFlags.Default)| 
      BindingFlags.NonPublic| 
      BindingFlags.Public| 
      BindingFlags.Instance; 

    target.GetType().InvokeMember(
     x.Name, invokeAttr, default(Binder), target, args); 
} 

se tirará si pasa un MemberInfo que no sea PropertyInfo o FieldInfo como primer argumento, porque BindingFlags.Default doesn' t especifica lo que vas a hacer.

Cuestiones relacionadas