7

我有一种情况,我想将 LinePragmas 添加到 CodeDom 对象。但有些代码 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;
}

更新: 我采用的解决方案是添加一个名为 Exists() 的扩展方法。我在这里写了一篇关于它的博客文章: Member Exists Dynamic C# 4.0

目的是创建一个扩展方法,该方法返回一个实现 DynamicObject 的 TryGetMember 的对象。它使用反射来返回真或假。它允许您编写如下代码:

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

6 回答 6

8

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

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

如果对象没有该属性,则 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 中可用的动态类型)。它当然不像动态解决方案那样干净/可读,但如果你不想抛出异常,它可能是要走的路。

编辑:正如@a​​rbiter 指出的那样,“这仅对本机.net 动态对象有效。例如,这对IDispatch 无效。”

于 2009-07-01T21:28:05.257 回答
5

我刚刚花了将近一个小时寻找在动态上获得某种 ruby​​ 风格的“RespondTo”方法的方法。肯定没有一个简单的答案,但我还没有放弃。

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

对于动态,到目前为止我唯一得到的是将您的对象视为动态的扩展方法。如果它有效,它会有效,如果不是它会默默地失败......

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

更新:

既然我投了反对票,那么你让我比扩展方法的微妙命名更简洁:它是炸药(Geddit?)!吞噬异常而无所作为是不好的。这不是生产代码,而是概念证明的第 1 版。我一直忘记你不能在像 stackoverflow 这样的成千上万的论坛上变得微妙。过失。

于 2009-07-01T21:47:30.100 回答
3

18 个月后......现在你真正想要的似乎已经发布了。它是TryGetMemberTryGetValue等……实际上,可能是TrySetMember,特别是。

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

于 2010-09-07T02:07:33.907 回答
-1

我要插话说静态类型会避免这个问题。

这是具有覆盖的抽象方法的候选者。

于 2009-07-01T22:49:49.523 回答
-1

想一想:由于目标类可以为不存在的成员(通过实现 IDynamicObject 或子类化 DynamicObject)提供自己的成员查找和调用实现,因此验证成员是否存在的唯一方法是调用它并查看对象是否处理它或抛出异常

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

- 编辑 -

如果您控制对象创建,则可以子类化该类并实现 IDynamicObject 以向您的其他类发出该方法不存在的信号。

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

于 2009-07-01T23:06:11.970 回答
-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`
}
于 2017-03-10T10:33:06.653 回答