在 DynamicObject 上添加操作
Adding Operation on DynamicObject
我有一个 DynamicObject 的子类,我在其中实现了几个隐式运算符(用于自动转换)并覆盖了一些 Try[OperationType] 方法。
当我尝试对该类型的对象进行 add(+) 操作时出现异常(问题底部的异常)。
...
var d = GetDynamicObject();
int result = d + 1;
...
我认为对这个问题有帮助的答案is this one。描述的行为是正确的,但我的动态对象定义了几个静态隐式运算符,我认为问题出在字符串 1 上。
当我为字符串定义隐式运算符时,que add(+) 操作抛出异常。
我可以通过堆栈跟踪观察到问题可能是他无法绑定运算符,我开始认为这是因为某种 add/concatenation 混淆。
我简化了我的代码并编写了这个测试,我认为它描述了这种情况。
当我评论字符串的隐式运算符时,一切都很好。
我是不是做错了什么?我还需要覆盖一些方法吗?
有什么想法吗?
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:
这是 a bug in the framework (Edit: Fixed in the corefx codebase,因此 .NET Core 2.0.4 及更高版本应该不会出现此问题)。我们可以看到 dynmic
没有 DynamicObject
:
时发生了同样的事情
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
。注释掉其中一个转换运算符,动态绑定器可以使用另一个。
问题是解决使用 +
的转换的处理会遇到一个差一错误。+
作为内置运算符用于加法和字符串连接。 31=]
如您所知,1 + 2
被视为加法,"abc" + x
被视为 string.Concat("abc", x)
。在 d + 1
的情况下,其中 d
是 dynamic
,活页夹需要弄清楚要使用哪个。
比较可能性时,一些 "simple" 目标类型在 table 中查找。如果可能的话,该目标的枚举值将低于给定值,所以很明显,首先要做的是看看是否是这种情况。
但不幸的是,该检查使用 <=
而不是 <
,但比较的限制比允许的最高限制多一个,因此 <
是正确的。而刚好超过限制的值恰好是string
。因此,如果您将动态联编程序置于它在转换为 "simple" 类型和转换为字符串之间进行选择的情况下,它决定它可以比较两种可能性,因为两者都是 "simple",试图找到查找 table 中的答案索引超出数组范围。根据版本的不同,它执行此操作并以 IndexOutOfRangeException
结束,或者它命中一个断言以确保它没有准确地执行它要执行的操作,并以 RuntimeBinderInternalCompilerException
.[=31 结束=]
我有一个 DynamicObject 的子类,我在其中实现了几个隐式运算符(用于自动转换)并覆盖了一些 Try[OperationType] 方法。
当我尝试对该类型的对象进行 add(+) 操作时出现异常(问题底部的异常)。
...
var d = GetDynamicObject();
int result = d + 1;
...
我认为对这个问题有帮助的答案is this one。描述的行为是正确的,但我的动态对象定义了几个静态隐式运算符,我认为问题出在字符串 1 上。 当我为字符串定义隐式运算符时,que add(+) 操作抛出异常。 我可以通过堆栈跟踪观察到问题可能是他无法绑定运算符,我开始认为这是因为某种 add/concatenation 混淆。
我简化了我的代码并编写了这个测试,我认为它描述了这种情况。 当我评论字符串的隐式运算符时,一切都很好。
我是不是做错了什么?我还需要覆盖一些方法吗? 有什么想法吗?
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:
这是 a bug in the framework (Edit: Fixed in the corefx codebase,因此 .NET Core 2.0.4 及更高版本应该不会出现此问题)。我们可以看到 dynmic
没有 DynamicObject
:
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
。注释掉其中一个转换运算符,动态绑定器可以使用另一个。
问题是解决使用 +
的转换的处理会遇到一个差一错误。+
作为内置运算符用于加法和字符串连接。 31=]
如您所知,1 + 2
被视为加法,"abc" + x
被视为 string.Concat("abc", x)
。在 d + 1
的情况下,其中 d
是 dynamic
,活页夹需要弄清楚要使用哪个。
比较可能性时,一些 "simple" 目标类型在 table 中查找。如果可能的话,该目标的枚举值将低于给定值,所以很明显,首先要做的是看看是否是这种情况。
但不幸的是,该检查使用 <=
而不是 <
,但比较的限制比允许的最高限制多一个,因此 <
是正确的。而刚好超过限制的值恰好是string
。因此,如果您将动态联编程序置于它在转换为 "simple" 类型和转换为字符串之间进行选择的情况下,它决定它可以比较两种可能性,因为两者都是 "simple",试图找到查找 table 中的答案索引超出数组范围。根据版本的不同,它执行此操作并以 IndexOutOfRangeException
结束,或者它命中一个断言以确保它没有准确地执行它要执行的操作,并以 RuntimeBinderInternalCompilerException
.[=31 结束=]