2009-07-01 17 views
7

Tengo una situación en la que deseo agregar LinePragmas a objetos CodeDom. Pero algunos objetos Dom de código tienen la propiedad LinePragma y otros no.C# 4.0, detectar si falta un método

Me pregunto si es posible usar la palabra clave dinámica para detectar si la propiedad existe en el objeto (sin lanzar una excepción) y si lo hace, agregue el pragma. Aquí está mi método actual:

public static T SetSource<T>(this T codeObject, INode sourceNode) 
    where T : CodeObject 
{ 
    codeObject.UserData["Node"] = sourceNode.Source; 
    dynamic dynamicCodeObject = codeObject; 

    // How can I not throw an exception here? 
    if (dynamicCodeObject.LinePragma != null) 
    { 
     dynamicCodeObject.LinePragma = new CodeLinePragma(
     sourceNode.Source.Path.AbsoluteUri, 
     sourceNode.Source.StartLine); 
    } 

    return codeObject; 
} 

ACTUALIZACIÓN: La solución fui con era añadir un método de extensión llamada existe(). Escribí un post sobre ello aquí: Member Exists Dynamic C# 4.0

El jist era crear un método de extensión que devuelve un objeto que implementa TryGetMember de DynamicObject. Utiliza la reflexión para luego devolver verdadero o falso. Lo cual le permite escribir código como este:

object instance = new { Foo = "Hello World!" }; 
if (instance.Reflection().Exists().Foo) 
{ 
    string value = instance.Reflection().Call().Foo; 
    Console.WriteLine(value); 
} 

Respuesta

8

puede detectar si un objeto tiene una propiedad sin tener que usar las características dinámicas de C# 4.0 - en lugar de utilizar las características de reflexión que han estado alrededor por un tiempo (I saber por lo menos .NET 2.0, no está seguro acerca < 2,0)

PropertyInfo info = codeObject.getType().GetProperty(
    "LinePragma", 
    BindingFlags.Public | BindingFlags.Instance 
) 

Si el objeto no tiene la propiedad, entonces GetProperty() devolverá nulo. Puede hacer lo mismo para campos (GetField()) y métodos (GetMethod()).

No sólo eso, sino que una vez que tenga la PropertyInfo, se puede utilizar directamente para hacer su conjunto:

info.SetValue(codeObject, new CodeLinePragma(), null); 

Si no está seguro de si la propiedad tiene un método set, usted podría tomar la ruta aún más seguro:

MethodInfo method = info.GetSetMethod(); 
if(method != null) 
    method.Invoke(codeObject, new object[]{ new CodeLinePragma() }); 

Esto también le da la ventaja añadida de ser un poco más performante sobre la sobrecarga de búsqueda de la llamada dinámica (no puede encontrar una referencia de esa declaración, así que voy a flotar allí afuera).

Supongo que no responde su pregunta directamente, sino que es una solución alternativa para lograr el mismo objetivo. Todavía no he usado las características # 4.0 (a pesar de que soy un gran admirador de la mecanografía dinámica disponible en Ruby). Ciertamente no es tan limpio/legible como la solución dinámica, pero si no desea lanzar una excepción, puede ser el camino a seguir.

EDITAR: como @arbiter señala: "Esto es válido solo para objetos dinámicos .NET nativos. Esto no funcionará, por ejemplo, para IDispatch".

+1

Esto es válido sólo para los objetos dinámicos .net nativos. Esto no funcionará, por ejemplo, para IDispatch. – arbiter

+0

@arbiter ahh, buen punto. Demostrando claramente mi falta de experiencia en el uso de .NET 4.0. +1 – Matt

+0

No creo que esto funcione, consulte mi respuesta a continuación – zvolkov

4

Acabo de pasar casi una hora buscando formas de obtener algún tipo de método "RespondTo" ruby-esque en dinámico. Ciertamente no hay una respuesta fácil, pero aún no me he rendido.

El punto hecho en la reflexión debe ser lo que hay que probar.

Con dinámica, lo único que obtengo hasta ahora es un método de extensión que trata su objeto como dinámico. Si funciona, funciona, si no se produce un error en silencio ...

public static void Dynamight<T>(this T target, Action<dynamic> action) 
{ 
    dynamic d = target; 
    try 
    { 
    action(d); 
    } 
    catch (RuntimeBinderException) 
    { 
    //That was that, didn't work out 
    } 
} 

entonces usted puede hacer ...

string h = "Hello"; 
h.Dynamight(d => Console.WriteLine(d.Length)); //Prints out 5 
h.Dynamight(d => d.Foo()); //Nothing happens 

Actualización:

Desde que estoy recibiendo downvotes y qué-tener-me dejas ser más conciso que el nombramiento sutil del método de extensión: (? Geddit) Es dinamita! Gabullición de excepciones y no hacer nada es malo. Esto no es un código de producción, sino una versión 1 de un pico de una prueba de concepto. Sigo olvidando que no puedes ser sutil en un foro multimillonario como stackoverflow. Mea culpa.

+0

+1 Cosas geniales. ¿Pero cuándo comienza a ser demasiado inteligente? –

+1

No estoy seguro acerca de la falla silenciosa que se produce allí; parece configurar un escenario donde un error desaparecerá en un agujero negro de oscuridad. En Ruby todavía obtienes un error de tiempo de ejecución si no hay un método - * a menos que * la clase también defina el método method_missing. E incluso entonces, tiene la opción de manejarlo como mejor le parezca y aún así dejar caer un error de "método indefinido" si así lo desea. – Matt

+2

El método de extensión solo se traga una RuntimeBinderException, que es el comportamiento deseado en este escenario. Todas las demás excepciones pasarían por la pila de llamadas. No veo el problema aquí. –

-1

Voy a hacer sonar y decir que la tipificación estática evitaría este problema.

Este es un candidato para un método abstracto con anulación.

+0

Bueno, el problema aquí es que estoy tratando de acceder a una propiedad en objetos CodeDom. Si no está familiarizado, probablemente haya 50 de ellos y tal vez la mitad o más con la propiedad LinePragma. Desafortunadamente, esa propiedad no se encuentra en ningún tipo de base o interfaz compartidos en particular. Entonces, con un tipado fuerte, debes probar y fallar el lanzamiento de un montón de objetos para encontrar el correcto. Muy tedioso –

-1

Piénselo: dado que la clase objetivo puede proporcionar su propia implementación para la búsqueda de miembro y la invocación para miembros no existentes (implementando IDynamicObject o subclase DynamicObject) la única forma de verificar si un miembro existe es invocarlo y ver si el objeto lo maneja o arroja una excepción.

¡Una vez más, el manejo de miembros no existentes es DINÁMICO!

--EDIT--

Si el control de la creación de objetos, que podría subclase de la clase y poner en práctica IDynamicObject para señalar su otra clase que el método no existe.

Es injusto downvote la respuesta si se señala la verdad - es decir, que no hay y no puede haber un confiable manera para comprobar la existencia miembro en el entorno de envío dinámico que no sea invocando el miembro.

+0

No es mi tipo, tampoco puedo agregar interfaces. :( –

+0

Además, invocar a un miembro inexistente arroja una excepción. Necesito más de un tipo de cosa TryInvoke. –

+1

Todo esto está bien, pero la pregunta es sobre los objetos CodeDom, que no son dinámicos. Y él solo se preguntaba si C# 4 podría ayudar a descubrir la propiedad. Además, no hay necesidad de MAYÚSCULA UNA ORACIÓN COMPLETA SÓLO PARA INTENTAR HACER UN PUNTO –

3

18 meses después ... parece que lo que realmente quería está allí ahora que se ha lanzado. Es el TryGetMember, TryGetValue, etc ... en realidad, probablemente TrySetMember, específicamente.

http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject_members.aspx

+0

TryGetMember solo existe en objetos que heredan de DynamicObject aunque ... fundirlo en (dinámico) no es lo mismo que lanzarlo a (DynamicObject). Cuando lo lanzas a dinámico y tratar de llamar a los miembros en el que llamará TryGetValue en el contenedor dinámico y lanzar una excepción si falla. Supongo que es posible lanzarlo como "(DynamicOb objeto) (dinámico) obj "? –

-1
using System.Collections.Generic; 
using System.Linq.Expressions; 

namespace System.Dynamic 
{ 
    // 
    // Summary: 
    //  Provides a base class for specifying dynamic behavior at run time. This class 
    //  must be inherited from; you cannot instantiate it directly. 
    public class DynamicObject : IDynamicMetaObjectProvider 
    { 
     // 
     // Summary: 
     //  Enables derived types to initialize a new instance of the System.Dynamic.DynamicObject 
     //  type. 
     protected DynamicObject(); 

     // 
     // Summary: 
     //  Returns the enumeration of all dynamic member names. 
     // 
     // Returns: 
     //  A sequence that contains dynamic member names. 
     public virtual IEnumerable<string> GetDynamicMemberNames(); 
     // 
     // Summary: 
     //  Provides a System.Dynamic.DynamicMetaObject that dispatches to the dynamic virtual 
     //  methods. The object can be encapsulated inside another System.Dynamic.DynamicMetaObject 
     //  to provide custom behavior for individual actions. This method supports the Dynamic 
     //  Language Runtime infrastructure for language implementers and it is not intended 
     //  to be used directly from your code. 
     // 
     // Parameters: 
     // parameter: 
     //  The expression that represents System.Dynamic.DynamicMetaObject to dispatch to 
     //  the dynamic virtual methods. 
     // 
     // Returns: 
     //  An object of the System.Dynamic.DynamicMetaObject type. 
     public virtual DynamicMetaObject GetMetaObject(Expression parameter); 
     // 
     // Summary: 
     //  Provides implementation for binary operations. Classes derived from the System.Dynamic.DynamicObject 
     //  class can override this method to specify dynamic behavior for operations such 
     //  as addition and multiplication. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the binary operation. The binder.Operation property 
     //  returns an System.Linq.Expressions.ExpressionType object. For example, for the 
     //  sum = first + second statement, where first and second are derived from the DynamicObject 
     //  class, binder.Operation returns ExpressionType.Add. 
     // 
     // arg: 
     //  The right operand for the binary operation. For example, for the sum = first 
     //  + second statement, where first and second are derived from the DynamicObject 
     //  class, arg is equal to second. 
     // 
     // result: 
     //  The result of the binary operation. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result); 
     // 
     // Summary: 
     //  Provides implementation for type conversion operations. Classes derived from 
     //  the System.Dynamic.DynamicObject class can override this method to specify dynamic 
     //  behavior for operations that convert an object from one type to another. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the conversion operation. The binder.Type property 
     //  provides the type to which the object must be converted. For example, for the 
     //  statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), 
     //  where sampleObject is an instance of the class derived from the System.Dynamic.DynamicObject 
     //  class, binder.Type returns the System.String type. The binder.Explicit property 
     //  provides information about the kind of conversion that occurs. It returns true 
     //  for explicit conversion and false for implicit conversion. 
     // 
     // result: 
     //  The result of the type conversion operation. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TryConvert(ConvertBinder binder, out object result); 
     // 
     // Summary: 
     //  Provides the implementation for operations that initialize a new instance of 
     //  a dynamic object. This method is not intended for use in C# or Visual Basic. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the initialization operation. 
     // 
     // args: 
     //  The arguments that are passed to the object during initialization. For example, 
     //  for the new SampleType(100) operation, where SampleType is the type derived from 
     //  the System.Dynamic.DynamicObject class, args[0] is equal to 100. 
     // 
     // result: 
     //  The result of the initialization. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result); 
     // 
     // Summary: 
     //  Provides the implementation for operations that delete an object by index. This 
     //  method is not intended for use in C# or Visual Basic. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the deletion. 
     // 
     // indexes: 
     //  The indexes to be deleted. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes); 
     // 
     // Summary: 
     //  Provides the implementation for operations that delete an object member. This 
     //  method is not intended for use in C# or Visual Basic. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the deletion. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TryDeleteMember(DeleteMemberBinder binder); 
     // 
     // Summary: 
     //  Provides the implementation for operations that get a value by index. Classes 
     //  derived from the System.Dynamic.DynamicObject class can override this method 
     //  to specify dynamic behavior for indexing operations. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the operation. 
     // 
     // indexes: 
     //  The indexes that are used in the operation. For example, for the sampleObject[3] 
     //  operation in C# (sampleObject(3) in Visual Basic), where sampleObject is derived 
     //  from the DynamicObject class, indexes[0] is equal to 3. 
     // 
     // result: 
     //  The result of the index operation. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a run-time exception is thrown.) 
     public virtual bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result); 
     // 
     // Summary: 
     //  Provides the implementation for operations that get member values. Classes derived 
     //  from the System.Dynamic.DynamicObject class can override this method to specify 
     //  dynamic behavior for operations such as getting a value for a property. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the object that called the dynamic operation. The 
     //  binder.Name property provides the name of the member on which the dynamic operation 
     //  is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) 
     //  statement, where sampleObject is an instance of the class derived from the System.Dynamic.DynamicObject 
     //  class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies 
     //  whether the member name is case-sensitive. 
     // 
     // result: 
     //  The result of the get operation. For example, if the method is called for a property, 
     //  you can assign the property value to result. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a run-time exception is thrown.) 
     public virtual bool TryGetMember(GetMemberBinder binder, out object result); 
     // 
     // Summary: 
     //  Provides the implementation for operations that invoke an object. Classes derived 
     //  from the System.Dynamic.DynamicObject class can override this method to specify 
     //  dynamic behavior for operations such as invoking an object or a delegate. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the invoke operation. 
     // 
     // args: 
     //  The arguments that are passed to the object during the invoke operation. For 
     //  example, for the sampleObject(100) operation, where sampleObject is derived from 
     //  the System.Dynamic.DynamicObject class, args[0] is equal to 100. 
     // 
     // result: 
     //  The result of the object invocation. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown. 
     public virtual bool TryInvoke(InvokeBinder binder, object[] args, out object result); 
     // 
     // Summary: 
     //  Provides the implementation for operations that invoke a member. Classes derived 
     //  from the System.Dynamic.DynamicObject class can override this method to specify 
     //  dynamic behavior for operations such as calling a method. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the dynamic operation. The binder.Name property provides 
     //  the name of the member on which the dynamic operation is performed. For example, 
     //  for the statement sampleObject.SampleMethod(100), where sampleObject is an instance 
     //  of the class derived from the System.Dynamic.DynamicObject class, binder.Name 
     //  returns "SampleMethod". The binder.IgnoreCase property specifies whether the 
     //  member name is case-sensitive. 
     // 
     // args: 
     //  The arguments that are passed to the object member during the invoke operation. 
     //  For example, for the statement sampleObject.SampleMethod(100), where sampleObject 
     //  is derived from the System.Dynamic.DynamicObject class, args[0] is equal to 100. 
     // 
     // result: 
     //  The result of the member invocation. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result); 
     // 
     // Summary: 
     //  Provides the implementation for operations that set a value by index. Classes 
     //  derived from the System.Dynamic.DynamicObject class can override this method 
     //  to specify dynamic behavior for operations that access objects by a specified 
     //  index. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the operation. 
     // 
     // indexes: 
     //  The indexes that are used in the operation. For example, for the sampleObject[3] 
     //  = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject 
     //  is derived from the System.Dynamic.DynamicObject class, indexes[0] is equal to 
     //  3. 
     // 
     // value: 
     //  The value to set to the object that has the specified index. For example, for 
     //  the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), 
     //  where sampleObject is derived from the System.Dynamic.DynamicObject class, value 
     //  is equal to 10. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown. 
     public virtual bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value); 
     // 
     // Summary: 
     //  Provides the implementation for operations that set member values. Classes derived 
     //  from the System.Dynamic.DynamicObject class can override this method to specify 
     //  dynamic behavior for operations such as setting a value for a property. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the object that called the dynamic operation. The 
     //  binder.Name property provides the name of the member to which the value is being 
     //  assigned. For example, for the statement sampleObject.SampleProperty = "Test", 
     //  where sampleObject is an instance of the class derived from the System.Dynamic.DynamicObject 
     //  class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies 
     //  whether the member name is case-sensitive. 
     // 
     // value: 
     //  The value to set to the member. For example, for sampleObject.SampleProperty 
     //  = "Test", where sampleObject is an instance of the class derived from the System.Dynamic.DynamicObject 
     //  class, the value is "Test". 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TrySetMember(SetMemberBinder binder, object value); 
     // 
     // Summary: 
     //  Provides implementation for unary operations. Classes derived from the System.Dynamic.DynamicObject 
     //  class can override this method to specify dynamic behavior for operations such 
     //  as negation, increment, or decrement. 
     // 
     // Parameters: 
     // binder: 
     //  Provides information about the unary operation. The binder.Operation property 
     //  returns an System.Linq.Expressions.ExpressionType object. For example, for the 
     //  negativeNumber = -number statement, where number is derived from the DynamicObject 
     //  class, binder.Operation returns "Negate". 
     // 
     // result: 
     //  The result of the unary operation. 
     // 
     // Returns: 
     //  true if the operation is successful; otherwise, false. If this method returns 
     //  false, the run-time binder of the language determines the behavior. (In most 
     //  cases, a language-specific run-time exception is thrown.) 
     public virtual bool TryUnaryOperation(UnaryOperationBinder binder, out object result); 
    }`enter code here` 
} 
Cuestiones relacionadas