una propiedades objetos dinamicamente crear clase c# dynamic dynamic-keyword

propiedades - crear objetos dinamicamente c#



Probar si una propiedad está disponible en una variable dinámica (12)

Aquí está la otra manera:

using Newtonsoft.Json.Linq; internal class DymanicTest { public static string Json = @"{ ""AED"": 3.672825, ""AFN"": 56.982875, ""ALL"": 110.252599, ""AMD"": 408.222002, ""ANG"": 1.78704, ""AOA"": 98.192249, ""ARS"": 8.44469 }"; public static void Run() { dynamic dynamicObject = JObject.Parse(Json); foreach (JProperty variable in dynamicObject) { if (variable.Name == "AMD") { var value = variable.Value; } } } }

Mi situación es muy simple. En algún lugar de mi código tengo esto:

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame(); //How to do this? if (myVariable.MyProperty.Exists) //Do stuff

Entonces, básicamente mi pregunta es cómo verificar (sin lanzar una excepción) que una determinada propiedad está disponible en mi variable dinámica. Podría hacer GetType() pero preferiría evitar eso ya que realmente no necesito saber el tipo de objeto. Todo lo que realmente quiero saber es si una propiedad (o un método, si eso hace la vida más fácil) está disponible. Cualquier punteros?


Bueno, me enfrenté a un problema similar pero en pruebas de unidad.

Usando SharpTestsEx puede verificar si existe una propiedad. Uso esta prueba de mis controladores, porque como el objeto JSON es dinámico, alguien puede cambiar el nombre y olvidarse de cambiarlo en el javascript o algo así, por lo que probar todas las propiedades al escribir el controlador debería aumentar mi seguridad.

Ejemplo:

dynamic testedObject = new ExpandoObject(); testedObject.MyName = "I am a testing object";

Ahora, usando SharTestsEx:

Executing.This(delegate {var unused = testedObject.MyName; }).Should().NotThrow(); Executing.This(delegate {var unused = testedObject.NotExistingProperty; }).Should().Throw();

Usando esto, pruebo todas las propiedades existentes usando "Should (). NotThrow ()".

Probablemente esté fuera de tema, pero puede ser útil para alguien.


Creo que no hay manera de averiguar si una variable dynamic tiene un determinado miembro sin intentar acceder a ella, a menos que se vuelva a implementar la forma en que se maneja el enlace dinámico en el compilador de C #. Lo que probablemente incluiría muchas conjeturas, porque está definido por la implementación, de acuerdo con la especificación de C #.

Por lo tanto, debería intentar acceder al miembro y capturar una excepción, si falla:

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame(); try { var x = myVariable.MyProperty; // do stuff with x } catch (RuntimeBinderException) { // MyProperty doesn''t exist }


En mi caso, necesitaba verificar la existencia de un método con un nombre específico, así que usé una interfaz para eso

var plugin = this.pluginFinder.GetPluginIfInstalled<IPlugin>(pluginName) as dynamic; if (plugin != null && plugin is ICustomPluginAction) { plugin.CustomPluginAction(action); }

Además, las interfaces pueden contener más que solo métodos:

Las interfaces pueden contener métodos, propiedades, eventos, indexadores o cualquier combinación de esos cuatro tipos de miembros.

Desde: Interfaces (Guía de programación C #)

Elegante y sin necesidad de atrapar excepciones o jugar con reflexión ...


La respuesta de Denis me hizo pensar en otra solución usando JsonObjects,

un comprobador de propiedad de encabezado:

Predicate<object> hasHeader = jsonObject => ((JObject)jsonObject).OfType<JProperty>() .Any(prop => prop.Name == "header");

o tal vez mejor:

Predicate<object> hasHeader = jsonObject => ((JObject)jsonObject).Property("header") != null;

por ejemplo:

dynamic json = JsonConvert.DeserializeObject(data); string header = hasHeader(json) ? json.header : null;


Las dos soluciones comunes a esto incluyen hacer la llamada y capturar la RuntimeBinderException , usar la reflexión para verificar la llamada, o serializar un formato de texto y analizar desde allí. El problema con las excepciones es que son muy lentas, porque cuando se construye una, la pila de llamadas actual se serializa. Serializar a JSON o algo análogo conlleva una penalización similar. Esto nos deja con una reflexión, pero solo funciona si el objeto subyacente es en realidad un POCO con miembros reales en él. Si se trata de un contenedor dinámico alrededor de un diccionario, un objeto COM o un servicio web externo, la reflexión no ayudará.

Otra solución es utilizar DynamicMetaObject para obtener los nombres de los miembros tal como los ve el DLR. En el siguiente ejemplo, uso una clase estática ( Dynamic ) para probar el campo Age y mostrarlo.

class Program { static void Main() { dynamic x = new ExpandoObject(); x.Name = "Damian Powell"; x.Age = "21 (probably)"; if (Dynamic.HasMember(x, "Age")) { Console.WriteLine("Age={0}", x.Age); } } } public static class Dynamic { public static bool HasMember(object dynObj, string memberName) { return GetMemberNames(dynObj).Contains(memberName); } public static IEnumerable<string> GetMemberNames(object dynObj) { var metaObjProvider = dynObj as IDynamicMetaObjectProvider; if (null == metaObjProvider) throw new InvalidOperationException( "The supplied object must be a dynamic object " + "(i.e. it must implement IDynamicMetaObjectProvider)" ); var metaObj = metaObjProvider.GetMetaObject( Expression.Constant(metaObjProvider) ); var memberNames = metaObj.GetDynamicMemberNames(); return memberNames; } }


Para mi esto funciona:

if (IsProperty(() => DynamicObject.MyProperty)) ; // do stuff delegate string GetValueDelegate(); private bool IsProperty(GetValueDelegate getValueMethod) { try { //we''re not interesting in the return value. //What we need to know is whether an exception occurred or not var v = getValueMethod(); return (v == null) ? false : true; } catch (RuntimeBinderException) { return false; } catch { return true; } }


Pensé que haría una comparación de la respuesta de Martijn y la respuesta de svick ...

El siguiente programa devuelve los siguientes resultados:

Testing with exception: 2430985 ticks Testing with reflection: 155570 ticks

void Main() { var random = new Random(Environment.TickCount); dynamic test = new Test(); var sw = new Stopwatch(); sw.Start(); for (int i = 0; i < 100000; i++) { TestWithException(test, FlipCoin(random)); } sw.Stop(); Console.WriteLine("Testing with exception: " + sw.ElapsedTicks.ToString() + " ticks"); sw.Restart(); for (int i = 0; i < 100000; i++) { TestWithReflection(test, FlipCoin(random)); } sw.Stop(); Console.WriteLine("Testing with reflection: " + sw.ElapsedTicks.ToString() + " ticks"); } class Test { public bool Exists { get { return true; } } } bool FlipCoin(Random random) { return random.Next(2) == 0; } bool TestWithException(dynamic d, bool useExisting) { try { bool result = useExisting ? d.Exists : d.DoesntExist; return true; } catch (Exception) { return false; } } bool TestWithReflection(dynamic d, bool useExisting) { Type type = d.GetType(); return type.GetProperties().Any(p => p.Name.Equals(useExisting ? "Exists" : "DoesntExist")); }

Como resultado sugeriría usar la reflexión. Vea abajo.

Respondiendo al comentario de bland:

Las proporciones son reflection:exception tics de reflection:exception para 100000 iteraciones:

Fails 1/1: - 1:43 ticks Fails 1/2: - 1:22 ticks Fails 1/3: - 1:14 ticks Fails 1/5: - 1:9 ticks Fails 1/7: - 1:7 ticks Fails 1/13: - 1:4 ticks Fails 1/17: - 1:3 ticks Fails 1/23: - 1:2 ticks ... Fails 1/43: - 1:2 ticks Fails 1/47: - 1:1 ticks

... justo - si esperas que falle con una probabilidad con menos de ~ 1/47, ve por una excepción.

Lo anterior supone que estás ejecutando GetProperties() cada vez. Es posible que pueda acelerar el proceso almacenando en caché el resultado de GetProperties() para cada tipo en un diccionario o similar. Esto puede ayudar si está verificando el mismo conjunto de tipos una y otra vez.


Por si acaso ayuda a alguien:

Si el método GetDataThatLooksVerySimilarButNotTheSame() devuelve un objeto ExpandoObject , también puede convertirlo en un IDictionary antes de verificarlo.

dynamic test = new System.Dynamic.ExpandoObject(); test.foo = "bar"; if (((IDictionary<string, object>)test).ContainsKey("foo")) { Console.WriteLine(test.foo); }


Si controla el tipo que se utiliza como dinámico, ¿no podría devolver una tupla en lugar de un valor para cada acceso de propiedad? Algo como...

public class DynamicValue<T> { internal DynamicValue(T value, bool exists) { Value = value; Exists = exists; } T Value { get; private set; } bool Exists { get; private set; } }

Posiblemente una implementación ingenua, pero si construye uno de estos internamente cada vez y devuelve ese valor en lugar del valor real, puede verificar la Exists de cada acceso a la propiedad y luego presionar Value si lo hace con el valor default(T) (e irrelevante) si no lo hace

Dicho esto, puede que me falte algo de conocimiento sobre cómo funciona la dinámica y esto podría no ser una sugerencia viable.


Siguiendo con la respuesta de @karask, podría ajustar la función como un ayudante así:

public static bool HasProperty(ExpandoObject expandoObj, string name) { return ((IDictionary<string, object>)expandoObj).ContainsKey(name); }


Tal vez use la reflexión?

dynamic myVar = GetDataThatLooksVerySimilarButNotTheSame(); Type typeOfDynamic = myVar.GetType(); bool exist = typeOfDynamic.GetProperties().Where(p => p.Name.Equals("PropertyName")).Any();