una - Obtener el valor de la propiedad de la cadena mediante la reflexión en C#
string c# ejemplos (21)
¿Qué hay de usar el CallByName
del espacio de nombres Microsoft.VisualBasic.dll
( Microsoft.VisualBasic.dll
)? Utiliza la reflexión para obtener propiedades, campos y métodos de objetos normales, objetos COM e incluso objetos dinámicos.
using Microsoft.VisualBasic;
using Microsoft.VisualBasic.CompilerServices;
y entonces
Versioned.CallByName(this, "method/function/prop name", CallType.Get).ToString();
Estoy intentando implementar la transformación de datos usando el ejemplo de Reflection 1 en mi código.
La función GetSourceValue
tiene un conmutador que compara varios tipos, pero quiero eliminar estos tipos y propiedades y hacer que GetSourceValue
obtenga el valor de la propiedad utilizando solo una única cadena como parámetro. Quiero pasar una clase y una propiedad en la cadena y resolver el valor de la propiedad.
es posible?
1 versión del archivo web de la publicación original del blog
Añadir a cualquier Class
:
public class Foo
{
public object this[string propertyName]
{
get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
}
public string Bar { get; set; }
}
Entonces, puedes usar como:
Foo f = new Foo();
// Set
f["Bar"] = "asdf";
// Get
string s = (string)f["Bar"];
Acerca de la discusión de propiedades anidadas, puede evitar todas las cosas de reflexión si usa el DataBinder.Eval Method (Object, String)
como se muestra a continuación:
var value = DataBinder.Eval(DateTime.Now, "TimeOfDay.Hours");
Por supuesto, deberá agregar una referencia al ensamblado de System.Web
, pero esto probablemente no sea un gran problema.
Aquí está mi solución. Funciona también con objetos COM y permite acceder a elementos de colección / matriz desde objetos COM.
public static object GetPropValue(this object obj, string name)
{
foreach (string part in name.Split(''.''))
{
if (obj == null) { return null; }
Type type = obj.GetType();
if (type.Name == "__ComObject")
{
if (part.Contains(''[''))
{
string partWithoundIndex = part;
int index = ParseIndexFromPropertyName(ref partWithoundIndex);
obj = Versioned.CallByName(obj, partWithoundIndex, CallType.Get, index);
}
else
{
obj = Versioned.CallByName(obj, part, CallType.Get);
}
}
else
{
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
}
return obj;
}
private static int ParseIndexFromPropertyName(ref string name)
{
int index = -1;
int s = name.IndexOf(''['') + 1;
int e = name.IndexOf('']'');
if (e < s)
{
throw new ArgumentException();
}
string tmp = name.Substring(s, e - s);
index = Convert.ToInt32(tmp);
name = name.Substring(0, s - 1);
return index;
}
Eche un vistazo a la biblioteca de Heleonix.Reflection . Puede obtener / establecer / invocar miembros por rutas, o crear un getter / setter (lambda compilado en un delegado) que es más rápido que la reflexión. Por ejemplo:
var success = Reflector.Get(DateTime.Now, null, "Date.Year", out int value);
O cree un getter una vez y caché para reutilizarlo (esto es más eficaz pero podría lanzar NullReferenceException si un miembro intermedio es nulo):
var getter = Reflector.CreateGetter<DateTime, int>("Date.Year", typeof(DateTime));
getter(DateTime.Now);
O si desea crear una List<Action<object, object>>
de diferentes captadores, solo especifique los tipos de base para los delegados compilados (las conversiones de tipo se agregarán a los lambdas compilados):
var getter = Reflector.CreateGetter<object, object>("Date.Year", typeof(DateTime));
getter(DateTime.Now);
El método para llamar ha cambiado en .NET Standard (a partir de 1.6). También podemos usar el operador condicional nulo de C # 6.
using System.Reflection;
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetRuntimeProperty(propName)?.GetValue(src);
}
El siguiente código es un método recursivo para mostrar la jerarquía completa de todos los nombres y valores de propiedad contenidos en la instancia de un objeto. Este método utiliza una versión simplificada de la respuesta GetPropertyValue()
de AlexD que GetPropertyValue()
arriba en este hilo. Gracias a este hilo de discusión, ¡pude averiguar cómo hacer esto!
Por ejemplo, uso este método para mostrar una explosión o volcado de todas las propiedades en una respuesta de servicio WebService
llamando al método de la siguiente manera:
PropertyValues_byRecursion("Response", response, false);
public static object GetPropertyValue(object srcObj, string propertyName)
{
if (srcObj == null)
{
return null;
}
PropertyInfo pi = srcObj.GetType().GetProperty(propertyName.Replace("[]", ""));
if (pi == null)
{
return null;
}
return pi.GetValue(srcObj);
}
public static void PropertyValues_byRecursion(string parentPath, object parentObj, bool showNullValues)
{
/// Processes all of the objects contained in the parent object.
/// If an object has a Property Value, then the value is written to the Console
/// Else if the object is a container, then this method is called recursively
/// using the current path and current object as parameters
// Note: If you do not want to see null values, set showNullValues = false
foreach (PropertyInfo pi in parentObj.GetType().GetTypeInfo().GetProperties())
{
// Build the current object property''s namespace path.
// Recursion extends this to be the property''s full namespace path.
string currentPath = parentPath + "." + pi.Name;
// Get the selected property''s value as an object
object myPropertyValue = GetPropertyValue(parentObj, pi.Name);
if (myPropertyValue == null)
{
// Instance of Property does not exist
if (showNullValues)
{
Console.WriteLine(currentPath + " = null");
// Note: If you are replacing these Console.Write... methods callback methods,
// consider passing DBNull.Value instead of null in any method object parameters.
}
}
else if (myPropertyValue.GetType().IsArray)
{
// myPropertyValue is an object instance of an Array of business objects.
// Initialize an array index variable so we can show NamespacePath[idx] in the results.
int idx = 0;
foreach (object business in (Array)myPropertyValue)
{
if (business == null)
{
// Instance of Property does not exist
// Not sure if this is possible in this context.
if (showNullValues)
{
Console.WriteLine(currentPath + "[" + idx.ToString() + "]" + " = null");
}
}
else if (business.GetType().IsArray)
{
// myPropertyValue[idx] is another Array!
// Let recursion process it.
PropertyValues_byRecursion(currentPath + "[" + idx.ToString() + "]", business, showNullValues);
}
else if (business.GetType().IsSealed)
{
// Display the Full Property Path and its Value
Console.WriteLine(currentPath + "[" + idx.ToString() + "] = " + business.ToString());
}
else
{
// Unsealed Type Properties can contain child objects.
// Recurse into my property value object to process its properties and child objects.
PropertyValues_byRecursion(currentPath + "[" + idx.ToString() + "]", business, showNullValues);
}
idx++;
}
}
else if (myPropertyValue.GetType().IsSealed)
{
// myPropertyValue is a simple value
Console.WriteLine(currentPath + " = " + myPropertyValue.ToString());
}
else
{
// Unsealed Type Properties can contain child objects.
// Recurse into my property value object to process its properties and child objects.
PropertyValues_byRecursion(currentPath, myPropertyValue, showNullValues);
}
}
}
El siguiente método funciona perfecto para mí:
class MyClass {
public string prop1 { set; get; }
public object this[string propertyName]
{
get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
}
}
Para obtener el valor de la propiedad:
MyClass t1 = new MyClass();
...
string value = t1["prop1].ToString();
Para establecer el valor de la propiedad:
t1["prop1] = value;
Esta es otra forma de encontrar una propiedad anidada que no requiere que la cadena le indique la ruta de anidamiento. Crédito a Ed S. por el método de propiedad única.
public static T FindNestedPropertyValue<T, N>(N model, string propName) {
T retVal = default(T);
bool found = false;
PropertyInfo[] properties = typeof(N).GetProperties();
foreach (PropertyInfo property in properties) {
var currentProperty = property.GetValue(model, null);
if (!found) {
try {
retVal = GetPropValue<T>(currentProperty, propName);
found = true;
} catch { }
}
}
if (!found) {
throw new Exception("Unable to find property: " + propName);
}
return retVal;
}
public static T GetPropValue<T>(object srcObject, string propName) {
return (T)srcObject.GetType().GetProperty(propName).GetValue(srcObject, null);
}
Gran respuesta por jheddings. Me gustaría mejorarlo permitiendo la referenciación de matrices agregadas o colecciones de objetos, para que propertyName pueda ser property1.property2 [X] .property3:
public static object GetPropertyValue(object srcobj, string propertyName)
{
if (srcobj == null)
return null;
object obj = srcobj;
// Split property name to parts (propertyName could be hierarchical, like obj.subobj.subobj.property
string[] propertyNameParts = propertyName.Split(''.'');
foreach (string propertyNamePart in propertyNameParts)
{
if (obj == null) return null;
// propertyNamePart could contain reference to specific
// element (by index) inside a collection
if (!propertyNamePart.Contains("["))
{
PropertyInfo pi = obj.GetType().GetProperty(propertyNamePart);
if (pi == null) return null;
obj = pi.GetValue(obj, null);
}
else
{ // propertyNamePart is areference to specific element
// (by index) inside a collection
// like AggregatedCollection[123]
// get collection name and element index
int indexStart = propertyNamePart.IndexOf("[")+1;
string collectionPropertyName = propertyNamePart.Substring(0, indexStart-1);
int collectionElementIndex = Int32.Parse(propertyNamePart.Substring(indexStart, propertyNamePart.Length-indexStart-1));
// get collection object
PropertyInfo pi = obj.GetType().GetProperty(collectionPropertyName);
if (pi == null) return null;
object unknownCollection = pi.GetValue(obj, null);
// try to process the collection as array
if (unknownCollection.GetType().IsArray)
{
object[] collectionAsArray = unknownCollection as Array[];
obj = collectionAsArray[collectionElementIndex];
}
else
{
// try to process the collection as IList
System.Collections.IList collectionAsList = unknownCollection as System.Collections.IList;
if (collectionAsList != null)
{
obj = collectionAsList[collectionElementIndex];
}
else
{
// ??? Unsupported collection type
}
}
}
}
return obj;
}
Nunca mencionas qué objeto estás inspeccionando, y como estás rechazando los que hacen referencia a un objeto dado, asumiré que te refieres a uno estático.
using System.Reflection;
public object GetPropValue(string prop)
{
int splitPoint = prop.LastIndexOf(''.'');
Type type = Assembly.GetEntryAssembly().GetType(prop.Substring(0, splitPoint));
object obj = null;
return type.GetProperty(prop.Substring(splitPoint + 1)).GetValue(obj, null);
}
Tenga en cuenta que marqué el objeto que se está inspeccionando con la variable local obj
. null
significa estático, de lo contrario, configúralo como quieras También tenga en cuenta que GetEntryAssembly()
es uno de los pocos métodos disponibles para obtener el ensamblaje "en ejecución", es posible que desee jugar con él si tiene dificultades para cargar el tipo.
Qué tal algo como esto:
public static Object GetPropValue(this Object obj, String name) {
foreach (String part in name.Split(''.'')) {
if (obj == null) { return null; }
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
return obj;
}
public static T GetPropValue<T>(this Object obj, String name) {
Object retval = GetPropValue(obj, name);
if (retval == null) { return default(T); }
// throws InvalidCastException if types are incompatible
return (T) retval;
}
Esto te permitirá descender a las propiedades usando una sola cadena, como esta:
DateTime now = DateTime.Now;
int min = GetPropValue<int>(now, "TimeOfDay.Minutes");
int hrs = now.GetPropValue<int>("TimeOfDay.Hours");
Puedes usar estos métodos como métodos estáticos o extensiones.
Si uso el código de Ed S. obtengo
''ReflectionExtensions.GetProperty (Type, string)'' es inaccesible debido a su nivel de protección
Parece que GetProperty()
no está disponible en Xamarin.Forms. TargetFrameworkProfile
es Profile7
en mi biblioteca de clases portátil (.NET Framework 4.5, Windows 8, ASP.NET Core 1.0, Xamarin.Android, Xamarin.iOS, Xamarin.iOS Classic).
Ahora encontré una solución de trabajo:
using System.Linq;
using System.Reflection;
public static object GetPropValue(object source, string propertyName)
{
var property = source.GetType().GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, propertyName, StringComparison.OrdinalIgnoreCase));
return property?.GetValue(source);
}
Usando PropertyInfo del espacio de nombres System.Reflection . La reflexión se compila bien, sin importar a qué propiedad intentemos acceder. El error aparecerá durante el tiempo de ejecución.
public static object GetObjProperty(object obj, string property)
{
Type t = obj.GetType();
PropertyInfo p = t.GetProperty("Location");
Point location = (Point)p.GetValue(obj, null);
return location;
}
Funciona bien para obtener la propiedad Location de un objeto
Label1.Text = GetObjProperty(button1, "Location").ToString();
Obtendremos la ubicación: {X = 71, Y = 27} También podemos devolver location.X o location.Y de la misma manera.
camino más corto ....
var a = new Test { Id = 1 , Name = "A" , date = DateTime.Now};
var b = new Test { Id = 1 , Name = "AXXX", date = DateTime.Now };
var compare = string.Join("",a.GetType().GetProperties().Select(x => x.GetValue(a)).ToArray())==
string.Join("",b.GetType().GetProperties().Select(x => x.GetValue(b)).ToArray());
jheddings y AlexD escribieron excelentes respuestas sobre cómo resolver cadenas de propiedad. Me gustaría lanzar el mío en la mezcla, ya que escribí una biblioteca dedicada exactamente para ese propósito.
La clase principal de Pather.CSharp es Resolver
. Por defecto puede resolver propiedades, matriz y entradas de diccionario.
Así, por ejemplo, si tienes un objeto como este
var o = new { Property1 = new { Property2 = "value" } };
Y si quieres obtener Property2
, puedes hacerlo así:
IResolver resolver = new Resolver();
var path = "Property1.Property2";
object result = r.Resolve(o, path);
//=> "value"
Este es el ejemplo más básico de las rutas que puede resolver. Si desea ver qué más puede hacer, o cómo puede extenderlo, simplemente diríjase a su Pather.CSharp .
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
Por supuesto, usted querrá agregar validación y todo eso, pero eso es lo esencial.
Dim NewHandle As YourType = CType(Microsoft.VisualBasic.CallByName(ObjectThatContainsYourVariable, "YourVariableName", CallType), YourType)
public class YourClass
{
//Add below line in your class
public object this[string propertyName] => GetType().GetProperty(propertyName)?.GetValue(this, null);
public string SampleProperty { get; set; }
}
//And you can get value of any property like this.
var value = YourClass["SampleProperty"];
public static List<KeyValuePair<string, string>> GetProperties(object item) //where T : class
{
var result = new List<KeyValuePair<string, string>>();
if (item != null)
{
var type = item.GetType();
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var pi in properties)
{
var selfValue = type.GetProperty(pi.Name).GetValue(item, null);
if (selfValue != null)
{
result.Add(new KeyValuePair<string, string>(pi.Name, selfValue.ToString()));
}
else
{
result.Add(new KeyValuePair<string, string>(pi.Name, null));
}
}
}
return result;
}
Esta es una forma de obtener todas las propiedades con sus valores en una lista.
public static TValue GetFieldValue<TValue>(this object instance, string name)
{
var type = instance.GetType();
var field = type.GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.FieldType) && e.Name == name);
return (TValue)field?.GetValue(instance);
}
public static TValue GetPropertyValue<TValue>(this object instance, string name)
{
var type = instance.GetType();
var field = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.PropertyType) && e.Name == name);
return (TValue)field?.GetValue(instance);
}