【问题标题】:How can setting one c# method-scoped variable affect another?设置一个 c# 方法范围的变量如何影响另一个?
【发布时间】:2011-07-27 00:33:16
【问题描述】:

这个真的把我难住了。我正在与另一个开发人员一起工作,他打电话给我,因为他无法相信他所看到的。我们一起调试调试器,我也看到了,没有解释。这是场景。他正在编写一个通过自动生成的 COM 包装器与第 3 方 COM 对象交互的方法(通过添加 COM 组件作为引用简单地生成。这是他方法的顶部:

  public bool RefolderDocument(ref IManDocument oDoc)
    {
        string strCustom1 = (string) oDoc.GetAttributeValueByID(imProfileAttributeID.imProfileCustom1);
        string strCustom2 = (string) oDoc.GetAttributeValueByID(imProfileAttributeID.imProfileCustom2);

代码的目的是从“文档”对象 (oDoc) 中获取项目编号和子项目编号。

当您逐步完成时,会发生以下情况。在第一次分配 strCustom1 具有预期值“32344”(项目编号)后,strCustom2 为空,如预期的那样。第二次赋值后,strCustom2得到了子项目号“0002”——但是strCustom1已经改成了32334——改了一个字符!?

让我印象深刻的是某种老式的 c 语言堆栈溢出(即使托管应用程序与 COM 组件互操作,我也不会想到这种情况)。我们都很困惑。为了解决这个奇怪的问题,我尝试将第一个字符串的内容复制到另一个位置,如下所示:

  public bool RefolderDocument(ref IManDocument oDoc)
    {
        string strCustom1 = string.Copy((string)oDoc.GetAttributeValueByID(imProfileAttributeID.imProfileCustom1));
        string strCustom2 = string.Copy((string)oDoc.GetAttributeValueByID(imProfileAttributeID.imProfileCustom2));

同样的结果!在这一点上,我们抓住了救命稻草,将代码从 .NET 4 删除到 .NET 3.5 (CLR 2),但没有任何变化。一个可能相关的点是,这是一项服务,我们正在附加到服务流程。构建目标是 x86,服务位置肯定在 Debug 输出构建文件夹中。

对此有什么合乎逻辑的解释吗?我不知道如何继续。

【问题讨论】:

    标签: c# service clr debugging


    【解决方案1】:

    我遇到的问题与您描述的类似。

    在我的情况下,.NET/compiler/runtine/CLR 运行良好,唯一的问题是 Visual Studio 调试器显示不正确的值。尝试写下你的价值观,例如到 System.Diagnostics.Trace 或控制台,检查是否是您的情况。

    【讨论】:

    • 这很有趣。我不认为是这种情况,因为它看起来好像值确实发生了变化。事实上,我们仔细检查此代码部分的方式是因为一些检测到差异的审计代码。但是将其扔给 Trace 以确认它不是调试器显示问题并没有什么坏处。
    • 这也是我的猜测。在每个点将值写入日志,并以这种方式分析它们。我看不出 复制的 字符串如何改变。
    【解决方案2】:

    我不确切知道为什么或如何发生这种情况,我希望有人能提供答案。作为一种解决方法,我会将返回的字符串解析为 int,然后执行第二条语句。至少这可以解决价值损失问题。

    int Custom1 = int.Parse((string)oDoc.GetAttributeValueByID(imProfileAttributeID.imProfileCustom1));
    int Custom2 = int.Parse((string)oDoc.GetAttributeValueByID(imProfileAttributeID.imProfileCustom2));
    

    【讨论】:

      【解决方案3】:

      COM 对象可能没有遵循 COM 内存管理规则,它正在覆盖 CLR 认为它拥有的内存。像不为 GetEnumerator() 创建新对象这样简单的事情真的会搞砸 COM 互操作和它所做的缓存。

      您还必须意识到,COM 调用的实际工作方式是传递对字符串的内存引用。虽然你这样称呼它:

      str = oDoc.GetAttributeValueByID(imProfileAttributeID.imProfileCustom1)
      

      真的叫得更像:

      hresult = oDoc.GetAttributeValueByID(imProfileAttributeID.imProfileCustom1,&str)
      

      所以你可以看到 COM 对象有一个指向 CLR 内存的指针。

      换句话说,查看 COM 对象,而不是您的 .NET 代码。

      【讨论】:

      • 托尼,感谢您的回复。不幸的是,COM 对象归供应商所有,据说经过了时间测试。 :-)。有趣的是另一个变量中间单个字符的变化!也许建议的解决方法之一可以解决问题。
      • @Decker,是的,这很不幸。我遇到了许多“经过时间考验”的 COM dll 在 .NET 下失败的问题,因为它们不遵循 COM 互操作期望它们遵循的规则。如果我实施了复制变通办法,我会担心内存损坏 - 如果你幸运的话,这只是一个调试器问题(我只在 C++ 中看到过,而不是在 .NET 中看到过,但还有其他人在报告 .NET)。如果您使用 .NET 4.0 并通过 ole 自动化,我建议您尝试使用动态类型,但需要先了解更改数据的来源。
      【解决方案4】:

      看起来 strCustom1 和 strCustom2 都被设置为对 GetAttributeValueByID 结果的引用。我不知道为什么,看看这里有没有其他人(Paging Dr. Skeet 哈哈...)会很有趣。

      但在短期内,我认为您会发现这会为您解决问题...

       public bool RefolderDocument(ref IManDocument oDoc)
          {
              string strCustom1 = "" + string.Copy((string)oDoc.GetAttributeValueByID(imProfileAttributeID.imProfileCustom1));
              string strCustom2 = "" + string.Copy((string)oDoc.GetAttributeValueByID(imProfileAttributeID.imProfileCustom2));
      

      我的想法基本上是让它评估一个表达式,而不是直接使用引用...

      马丁。

      附言。 string.Copy() 是怎么回事?

      【讨论】:

      • 是的。 String.Copy 试图在源位置被覆盖之前移动 strCustom1 实际指向的位置。我希望这行得通。它没有。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-02
      • 1970-01-01
      • 1970-01-01
      • 2020-09-30
      • 2021-12-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多