【问题标题】:.NET implicit typing causes RuntimeBinder exception.NET 隐式类型导致 RuntimeBinder 异常
【发布时间】:2018-09-20 14:02:43
【问题描述】:

我正在使用名为 ArcObjects 和 .NET 4.5 的 API。我遇到了错误

'object' does not contain a definition for 'AddValue'

虽然使用 API 上的对象之一,但我认为这可能是一个比使用此 API 更普遍的 .NET 问题。这是在运行时发生的错误。

在某些背景下,ArcObjects 都是 COM 对象,因此 .NET 库只是这些对象的包装器。在使用 ArcObjects 时,我主要使用地理数据库,它基本上是一个常规 RDBS(具有空间组件),其中每一行都称为一个要素。

我的第一次尝试代码如下:

//this IUniqueValueRenderer is an ArcObject
var uvRenderer = new ESRI.ArcGIS.Carto.UniqueValueRendererClass() as IUniqueValueRenderer;

//...   set some parameters on the uvRenderer

//the error occurs on this line. SampleAreaFeature.get_Value(idIndex) returns the non-null integer 324
//SampleAreaFeature is a feature in the geodatabase
uvRenderer.AddValue(SampleAreaFeature.get_Value(1).ToString(), "SampleField", redSymbol as ISymbol);

AddValue 的定义是AddValue(string,string,ISymbol)。编译时不会出现错误或警告,并且可以在运行时通过反射找到 AddValue。

我可以通过将最后一行更改为以下内容来解决此问题:

string value = SampleAreaFeature.get_Value(idIndex).ToString();
uvRenderer.AddValue(value, "SampleField", redSymbol as ISymbol);

即使是陌生人以下也不起作用:

var value = SampleAreaFeature.get_Value(idIndex).ToString();
uvRenderer.AddValue(value, "SampleField", redSymbol as ISymbol);

这让我想到了手头的问题。为什么这三个 sn-ps 的编译方式不同?

未显式键入的值会产生不同的结果,两者都可以编译,但其中一个无法在运行时绑定到 AddValue 方法。

这里是使用 'var' 关键字时的 IL:

IL_00bb: call class ESRI.ArcGIS.Geodatabase.IFeature DataReviewModule.ModGlobals::get_SampleAreaFeature()
IL_00c0: ldloc.2
IL_00c1: callvirt instance object ESRI.ArcGIS.Geodatabase.IFeature::get_Value(int32)
IL_00c6: callvirt instance !2 class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, object>::Invoke(!0, !1)
IL_00cb: stloc.3
IL_00cc: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`5<class [System.Core]System.Runtime.CompilerServices.CallSite, class [ESRI.ArcGIS.Carto]ESRI.ArcGIS.Carto.IUniqueValueRenderer, object, string, class ESRI.ArcGIS.Display.ISymbol>> DataReviewModule.docWinReview/'<>o__127'::'<>p__1'
IL_00d1: brtrue.s IL_0125

IL_00d3: ldc.i4 256
IL_00d8: ldstr "AddValue"
IL_00dd: ldnull
IL_00de: ldtoken DataReviewModule.docWinReview
IL_00e3: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_00e8: ldc.i4.4
IL_00e9: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
IL_00ee: dup
IL_00ef: ldc.i4.0
IL_00f0: ldc.i4.1
IL_00f1: ldnull
IL_00f2: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)
IL_00f7: stelem.ref

这里是使用“字符串”关键字时的 IL:

IL_00f5: call class ESRI.ArcGIS.Geodatabase.IFeature DataReviewModule.ModGlobals::get_SampleAreaFeature()
IL_00fa: ldloc.2
IL_00fb: callvirt instance object ESRI.ArcGIS.Geodatabase.IFeature::get_Value(int32)
IL_0100: callvirt instance !2 class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, object>::Invoke(!0, !1)
IL_0105: callvirt instance !2 class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite, object, string>::Invoke(!0, !1)
IL_010a: stloc.3
IL_010b: ldloc.1
IL_010c: ldloc.3
IL_010d: ldsfld string DataReviewModule.ModGlobals::sampleIdField
IL_0112: ldarg.0
IL_0113: ldfld class ESRI.ArcGIS.Display.ISimpleFillSymbol DataReviewModule.docWinReview::redSymbol
IL_0118: isinst ESRI.ArcGIS.Display.ISymbol
IL_011d: callvirt instance void [ESRI.ArcGIS.Carto]ESRI.ArcGIS.Carto.IUniqueValueRenderer::AddValue(string, string, class ESRI.ArcGIS.Display.ISymbol)

【问题讨论】:

  • 这不是那个问题的重复。该问题的结论是编译后隐式和显式类型之间没有有意义的区别。而在这里,当使用 var 而不是显式类型时,我会得到不同的运行时行为。
  • 如果get_Value() 返回一个dynamic,或者一个隐藏.ToString() 的类型,编译会有所不同。该类型必须隐式转换为定义的字符串,但这是可能的。如果您使用的是 VS,请将鼠标悬停在 var 上。报告什么类型?
  • Microsoft.CSharp.RuntimeBinder 的存在为dynamic 的可能性提供了支持。
  • @recursive 是正确的,动态是阴影 ToString() 导致 var 是动态的。知道为什么 RuntimeBinder 会失败吗?我通常可以将动态对象传递给强类型函数。我猜这与绑定到底层 COM 对象失败有关。

标签: .net cil arcobjects


【解决方案1】:

SampleAreaFeature.get_Value(idIndex) 返回一个动态。

这导致var value 的类型为dynamic

关于 COM 绑定的一些规则我不明白,这导致 AddValue 的两个定义在这里发生冲突,导致没有动态解析。 COM 名称解析将所有接口混合在一起进行名称查找,这让我摆脱了麻烦,但似乎让你陷入了麻烦。

这个类也完全有可能不支持运行时动态调用。反射在表观类型上运行,而动态调用在实际类型上运行。如果这些不匹配,就会发生奇怪的事情,并且我的代码依赖于它们的不同来解决重复的 COM 接口 Guid(一个用户是 MS-Word,我不知道另一个用户是谁)。如果实际类型不支持动态解析,这将是预期的错误。

我个人建议通过 ((object)SampleAreaFeature.get_Value(idIndex)).ToString() 解决此问题,从而强制 .ToString() 直接解析,而不是通过 dynamic

【讨论】:

  • 这可能是它。但是null 不会阻止解决,除非有其他竞争匹配。
  • 有两个匹配项;一个在 COM 类上,一个在 COM 接口上。 COM 中没有显式实现。
  • .ToString() 不返回 null。 get_value() 返回整数 347,ToString() 返回“347”。
猜你喜欢
  • 2018-09-22
  • 1970-01-01
  • 2018-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-22
  • 2014-02-17
相关资源
最近更新 更多