【问题标题】:Adding Operation on DynamicObject在 DynamicObject 上添加操作
【发布时间】:2016-01-10 18:48:27
【问题描述】:

我有一个 DynamicObject 的子类,我在其中实现了几个隐式运算符(用于自动转换)并覆盖了一些 Try[OperationType] 方法。

当我尝试使用该类型的对象进行 add(+) 操作时出现异常(问题底部的异常)。

...
var d = GetDynamicObject();
int result = d + 1;
...

我认为对这个问题有帮助的答案is this one。描述的行为是正确的,但是我的动态对象定义了几个静态隐式运算符,我认为问题出在字符串之一。 当我为字符串定义隐式运算符时,que add(+) 操作会引发异常。 我可以通过堆栈跟踪观察到问题可能是他无法绑定运算符,我开始认为这是因为某种添加/连接混淆。

我简化了我的代码并编写了这个测试,我认为它描述了这种情况。 当我评论字符串的隐式运算符时,一切都很好。

我做错了吗?我还需要重写一些方法吗? 有什么想法吗?

public class Program
{
    static void Main(string[] args)
    {
        try
        {
            var d = GetDynamicObject();
            int result = d + 1;
            Console.WriteLine("OK: " + result);
        }
        catch (Exception)
        {
            Console.WriteLine("Error!");
        }

        Console.ReadLine();
    }

    public static dynamic GetDynamicObject()
    {
        return new MyDynamicObject();
    }
}


public class MyDynamicObject : DynamicObject
{
    public static implicit operator int(MyDynamicObject obj)
    {
        return 0;
    }

    public static implicit operator long(MyDynamicObject obj)
    {
        return 0;
    }

    public static implicit operator double(MyDynamicObject obj)
    {
        return 0;
    }

    // Works when commented
    public static implicit operator string(MyDynamicObject obj)
    {
        return null;
    }

    public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result)
    {
        Console.WriteLine("MyDynamicObject.TryBinaryOperation");
        return base.TryBinaryOperation(binder, arg, out result);
    }  
}

例外:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderInternalCompilerException was unhandled
  HResult=-2146233088
  Message=An unexpected exception occurred while binding a dynamic operation
  Source=Microsoft.CSharp
  StackTrace:
       at Microsoft.CSharp.RuntimeBinder.Semantics.ExpressionBinder.WhichSimpleConversionIsBetter(PredefinedType pt1, PredefinedType pt2)
       at Microsoft.CSharp.RuntimeBinder.Semantics.ExpressionBinder.WhichTypeIsBetter(PredefinedType pt1, PredefinedType pt2, CType typeGiven)
       at Microsoft.CSharp.RuntimeBinder.Semantics.ExpressionBinder.WhichBofsIsBetter(BinOpFullSig bofs1, BinOpFullSig bofs2, CType type1, CType type2)
       at Microsoft.CSharp.RuntimeBinder.Semantics.ExpressionBinder.FindBestSignatureInList(List`1 binopSignatures, BinOpArgInfo info)
       at Microsoft.CSharp.RuntimeBinder.Semantics.ExpressionBinder.BindStandardBinop(ExpressionKind ek, EXPR arg1, EXPR arg2)
       at Microsoft.CSharp.RuntimeBinder.RuntimeBinder.BindBinaryOperation(CSharpBinaryOperationBinder payload, ArgumentObject[] arguments, Dictionary`2 dictionary)
       at Microsoft.CSharp.RuntimeBinder.RuntimeBinder.DispatchPayload(DynamicMetaObjectBinder payload, ArgumentObject[] arguments, Dictionary`2 dictionary)
       at Microsoft.CSharp.RuntimeBinder.RuntimeBinder.BindCore(DynamicMetaObjectBinder payload, IEnumerable`1 parameters, DynamicMetaObject[] args, DynamicMetaObject& deferredBinding)
       at Microsoft.CSharp.RuntimeBinder.RuntimeBinder.Bind(DynamicMetaObjectBinder payload, IEnumerable`1 parameters, DynamicMetaObject[] args, DynamicMetaObject& deferredBinding)
       at Microsoft.CSharp.RuntimeBinder.BinderHelper.Bind(DynamicMetaObjectBinder action, RuntimeBinder binder, IEnumerable`1 args, IEnumerable`1 arginfos, DynamicMetaObject onBindingError)
       at Microsoft.CSharp.RuntimeBinder.CSharpBinaryOperationBinder.FallbackBinaryOperation(DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion)
       at System.Dynamic.DynamicObject.MetaDynamic.<>c__DisplayClass9_0.<BindBinaryOperation>b__0(DynamicMetaObject e)
       at System.Dynamic.DynamicObject.MetaDynamic.CallMethodWithResult(String methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback, Fallback fallbackInvoke)
       at System.Dynamic.DynamicObject.MetaDynamic.BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg)
       at System.Dynamic.BinaryOperationBinder.Bind(DynamicMetaObject target, DynamicMetaObject[] args)
       at System.Dynamic.DynamicMetaObjectBinder.Bind(Object[] args, ReadOnlyCollection`1 parameters, LabelTarget returnLabel)
       at System.Runtime.CompilerServices.CallSiteBinder.BindCore[T](CallSite`1 site, Object[] args)
       at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
       at WIG.Common.Tests.Program.Main(String[] args) in C:\WIG\WIG Framework\Dev\WIG.Common.Tests\Program.cs:line 74
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

【问题讨论】:

  • 尝试将对象显式转换为int,如下所示:int result = (int)d + 1;
  • 谢谢。是的,这行得通,我忘了提到我已经尝试过显式演员表并且行得通。这就是我怀疑问题与运行时表达式(操作、操作数、类型...)的解析有关的原因之一。我想让MyDynamicObject 的用户不注意类型转换,定义对象本身内部的所有行为(让他像动态一样使用对象)。为此,我只能访问隐式转换器和虚拟DynamicObject 方法。也许我正面临 RuntimeBinder 或其他组件的限制。

标签: c# dynamicobject


【解决方案1】:

这是a bug in the framework(编辑:Fixed in the corefx codebase,因此 .NET Core 2.0.4 及更高版本不应出现此问题)。我们可以看到在没有DynamicObject 的情况下使用dynmic 会发生同样的事情:

public class Castable
{
    public static implicit operator int(Castable obj) => 3;

    public static implicit operator string(Castable obj) => "abc";
}

…

dynamic d = new Castable();
var result = d + 2;    // Throws RuntimeBinderInternalCompilerException in NetFX
                       // IndexOutOfRangeException in CoreFX.

dynamic 更改为var,静态绑定器正在工作而不是动态绑定器(没有此错误)将result 设置为5。注释掉其中一个转换运算符,动态绑定器可以使用另一个。

问题在于,使用+(作为内置运算符用于加法和字符串连接)来解析使用哪个转换的处理会遇到一个错误。

如您所知,1 + 2 被视为加法,"abc" + x 被视为string.Concat("abc", x)。对于d + 1,其中ddynamic,活页夹需要确定使用哪个。

在比较可能性时,会在表格中查找一些“简单”的目标类型。如果这是可能的,那么该目标的枚举值将低于给定值,因此显然首先要做的就是看看是否是这种情况。

但不幸的是,该检查使用&lt;= 而不是&lt;,但与之比较的限制比允许的最高限制多一,因此&lt; 本来是正确的。刚好高于限制的值恰好是string。因此,如果您将动态绑定器置于在转换为“简单”类型和转换为字符串之间进行选择的情况下,它会决定将这两种可能性都比较为“简单”,并尝试在索引超出数组范围的查找表。根据版本的不同,它会执行此操作并使用IndexOutOfRangeException 炸毁,或者它会触发一个断言以确保它没有准确地执行它即将执行的操作,并使用RuntimeBinderInternalCompilerException 炸毁。

【讨论】:

    猜你喜欢
    • 2017-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-17
    • 1970-01-01
    • 2021-11-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多