同一对象的两个引用具有不同的属性值(C#)

4

我正在尝试追踪一个非常难以捉摸的应用程序中的bug,该应用程序操作FlowDocument。下面是三行连续的调试代码及其输出:

Debug.Assert(ReferenceEquals(document1, document2));
Debug.WriteLine(document1.Blocks.Count); // 1
Debug.WriteLine(document2.Blocks.Count); // 3

有人能帮我理解为什么两个引用同一个对象的给定属性可以具有不同的值吗?或者我对ReferenceEquals的工作方式有什么误解吗?

谢谢,

Tim

编辑:

如果我将断言更改为if块,则调试代码永远不会运行...

if (ReferenceEquals(document1, document2))
{
    Debug.WriteLine(document1.Blocks.Count);
    Debug.WriteLine(document2.Blocks.Count);
}

这让我感到非常愚蠢,因为ReferenceEquals测试显然是有效的,但我不明白为什么断言不起作用。


文档类是标准的System.Windows.Documents.FlowDocument,但我怀疑它所在的RichTextBox在复制/粘贴操作后创建了第二个实例(除了我的代码创建的实例之外)。这就是为什么我正在使用从不同来源获取的对象引用。 - Tim Coulter
2
奇怪。你能提供一些文档类的代码吗?如果document1和document2被声明为不同类型 - 比如Object和从Object继承的某个类型 - 那么可能存在一个覆盖“Blocks”或“Blocks.Count”的定义,这会改变调用哪个代码,即使引用指向同一个实例。 - 3Dave
1
如果它们被声明为相同的类型,这不应该发生。是否有一些正在运行的带外/异步进程可能会更改该值?读取该值是否会影响它(这似乎非常不可能)? - 3Dave
如果您在循环中运行此代码(例如,循环10次并继续调用Debug.WriteLine(...)),那么值是否稳定? - 3Dave
肯定没有其他线程可以访问流文档,而Blocks集合只是一个标准的框架集合类。 - Tim Coulter
我将我的东西移到答案中,以免评论线程变得混乱。 - 3Dave
4个回答

4

我能够想到的两件事情:

  • 访问 Blocks 或 Blocks.Count 可能会改变状态(虽然不应该,但确实有可能)。
  • 在两次调用之间,对象可能已在另一个线程上更改。你在应用程序中使用多线程吗?

此外,如果引用是不同类型的(即 document2 是继承类型),则属性可能会被重载以返回不同的内容。您可以检查 document1.GetType() == document2.GetType() 是否成立。

根据您的更新进行编辑

Debug.Assert 只有在程序集以调试模式编译时才会运行。如果您正在运行 Release 模式,则不会运行。这是因为 Debug.Assert 被装饰了 [Conditional("DEBUG")] 属性。

看起来问题是您确实有两个不同的对象。


是的,你说得对 - 看起来我有两个不同的对象。很抱歉用一个无根据的问题浪费了你的时间。 - Tim Coulter

2
如果一个属性有副作用,每次调用它时可能会产生不同的结果。例如,DateTime.Now 并不总是等于 DateTime.Now。
在不知道代码的任何其他信息的情况下,那就是我的猜测。
编辑:使用 Reflector 对 FlowDocument 进行分析显示,每次调用 Blocks 都会返回一个新实例。此外,BlockCollection 的 Count 属性相当复杂,因此我建议仔细检查它。不幸的是,我对涉及的类型不是很了解,所以无法立即告诉您出了什么问题。

Brian - 感谢你的见解。我不认为这是我报告的问题的直接原因(我的愚蠢应该为此负责),但它可能与我正在尝试找到的错误有关。再次感谢。 - Tim Coulter
没问题。我们都会犯这样的错误,偶尔一次。我很高兴你找到了解决方案。 - Brian Rasmussen

1
你确定 Blocks 对象引用指向同一个对象吗?尝试使用
Debug.Assert(ReferenceEquals(document1.Blocks, document2.Blocks));

并查看是否成功。


1

可能性(其中一些已在评论中被排除):

  1. 某个外部进程,比如正在将块加载到FlowDocument中的某些进程,在写入之间改变了值。

  2. 海森堡:读取Blocks属性会影响它。当从数据源读取行时,有时会发生这种情况。我不熟悉FlowDocument,所以不确定这是否可行。

  3. 如果实例被声明为不同类型,则它们的引用仍将相等,但Blocks(或Blocks.Count)的值可能会被覆盖,并导致不同的返回值,因为可能调用不同的代码,例如Object.ToString()与Int.ToString()。

  4. 你在循环的中间某个地方调用了这段调试代码。如果您在命令窗口或某个附加的调试器中而不是应用程序内运行它,则可能会发生这种情况。

  5. 您的屏幕上有死区,使第一个“3”看起来像“1”。

  6. 您住在核反应堆旁边。

尝试以下几点:

  1. 在循环中运行您的 .Assert 代码并查看值是否稳定。

  2. 在 Blocks 值上设置读/写断点。(我知道您可以在 C 中这样做,但尚未在 C# 中尝试过)

更新

关于您关于 .Assert() 不按预期工作的附加问题:

刚刚查看了 MSDN 上关于 Debug.Assert() 的 此注释

默认情况下,Debug.Assert 方法仅在调试版本中起作用。如果要在发布版本中进行断言,请使用 Trace.Assert 方法。有关更多信息,请参见托管代码中的断言。

您是在调试版本还是发布版本中运行?


抱歉,David,看起来我误导了你(请参见上面的编辑)。不过,知道为什么Assert不起作用仍然是很好的。你的答案6在那里是一个明显的可能性。 :) - Tim Coulter
我正在运行调试版本,但即使我在这里犯了错误,它难道不会同样影响Debug.WriteLine()吗? - Tim Coulter
无论如何,我会接受你的答案,因为你帮助我看到了我的逻辑漏洞(我向大家为浪费时间的问题道歉)。 - Tim Coulter
谢谢。 =)另一个想法是在".Assert"上设置断点并跟踪它。(我记得MS有一个源代码库可以用于跟踪框架代码。)此外,这不是浪费时间。 - 3Dave

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接