C#4.0,检测是否缺少方法

C#4.0,检测是否缺少方法,c#,dynamic,codedom,C#,Dynamic,Codedom,我有一种情况,我想向CodeDom对象添加LinePragmas。但有些代码dom对象具有LinePragma属性,有些则没有 所以我想知道是否可以使用dynamic关键字来检测对象上是否存在属性(而不引发异常),然后添加pragma。以下是我目前的方法: public static T SetSource<T>(this T codeObject, INode sourceNode) where T : CodeObject { codeObject.UserDat

我有一种情况,我想向CodeDom对象添加LinePragmas。但有些代码dom对象具有LinePragma属性,有些则没有

所以我想知道是否可以使用dynamic关键字来检测对象上是否存在属性(而不引发异常),然后添加pragma。以下是我目前的方法:

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;
}

您可以检测对象是否具有属性,而不必使用C#4.0的动态功能,而是使用已经存在了一段时间的反射功能(我至少知道.NET 2.0,不确定<2.0)

如果该对象没有该属性,则GetProperty()将返回null。可以对字段(GetField())和方法(GetMethod())执行类似的操作

不仅如此,一旦您拥有PropertyInfo,您就可以直接使用它进行设置:

info.SetValue(codeObject, new CodeLinePragma(), null);
如果不确定该属性是否具有set方法,则可以采取更安全的方法:

MethodInfo method = info.GetSetMethod();
if(method != null)
    method.Invoke(codeObject, new object[]{ new CodeLinePragma() });
与动态调用的查找开销相比,这还为您带来了额外的好处(无法找到该语句的引用,因此我将在那里浮动它)

我想这不是直接回答你的问题,而是实现同样目标的另一种解决方案。我还没有真正使用过#4.0特性(尽管我非常喜欢Ruby中的动态类型)。它当然不像动态解决方案那样干净/可读,但是如果您不想抛出异常,那么它可能是一种方法


编辑:正如@arbiter指出的,“这只对本机.net动态对象有效。例如,对于IDispatch,这将不起作用。”

我刚刚花了将近一个小时的时间寻找方法,在动态对象上获得某种ruby风格的“RespondTo”方法。答案当然不容易,但我还没有放弃

反思中提出的观点应该是值得尝试的

对于dynamic,到目前为止,我唯一得到的是一个将对象视为动态对象的扩展方法。如果成功了,它就成功了,如果失败了,它就会默默地失败

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
  }
}
更新:


既然我得到了反对票,还有什么比扩展方法的微妙命名更简洁的呢:它是dynamite(Geddit?)!狼吞虎咽异常而无所事事是不好的。这不是生产代码,而是概念验证的spike版本1。我一直忘了,在stackoverflow这样的数千个论坛上,你不可能表现得很微妙。这真是罪过。

我要插话说,静态键入可以避免这个问题


这是具有重写的抽象方法的候选对象。

考虑一下:因为目标类可以为非现有成员的成员查找和调用提供自己的实现(通过实现IDynamiObject或将DynamicObject子类化)验证成员是否存在的唯一方法是调用它并查看对象是否处理它或引发异常

再一次,对不存在成员的处理是动态的

--编辑--

如果您控制对象的创建,您可以对该类进行子类化并实现idynamiobject,以向您的另一个类发出该方法不存在的信号


如果答案指出了真相,即除了调用成员之外,在动态调度环境中没有也不可能有一种可靠的方法来检查成员的存在,那么对答案投反对票是不公平的

18个月后。。。看起来你真正想要的东西现在已经发布了。它是TryGetMemberTryGetValue,等等。。。事实上,具体来说,可能是

使用System.Collections.Generic;
使用System.Linq.Expressions;
名称空间系统。动态
{
//
//总结:
//提供用于在运行时指定动态行为的基类。此类
//必须从继承;不能直接实例化它。
公共类DynamicObject:IDynamicMetaObjectProvider
{
//
//总结:
//使派生类型能够初始化System.Dynamic.DynamicObject的新实例
//类型。
受保护的动态对象();
//
//总结:
//返回所有动态成员名称的枚举。
//
//返回:
//包含动态成员名称的序列。
公共虚拟IEnumerable GetDynamicMemberNames();
//
//总结:
//提供分派到动态虚拟机的System.Dynamic.DynamicMetaObject
//方法。该对象可以封装在另一个System.Dynamic.DynamicMetaObject中
//为单个操作提供自定义行为。此方法支持动态
//语言实现者的语言运行时基础设施
//直接从代码中使用。
//
//参数:
//参数:
//表示要分派到的System.Dynamic.DynamicMetaObject的表达式
//动态虚拟方法。
//
//返回:
//System.Dynamic.DynamicMetaObject类型的对象。
公共虚拟DynamicMetaObject GetMetaObject(表达式参数);
//
//总结:
//提供二进制操作的实现。派生自System.Dynamic.DynamicObject的类
//类可以重写此方法以指定此类操作的动态行为
//例如加法和乘法。
//
//参数:
//活页夹:
//提供有关二进制操作的信息。binder.operation属性
//返回System.Linq.Expressions.ExpressionType对象。例如
//sum=第一个+第二个语句,其中第一个和第二个语句是派生的
MethodInfo method = info.GetSetMethod();
if(method != null)
    method.Invoke(codeObject, new object[]{ new CodeLinePragma() });
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
  }
}
string h = "Hello";
h.Dynamight(d => Console.WriteLine(d.Length)); //Prints out 5
h.Dynamight(d => d.Foo()); //Nothing happens
    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`
}