views:

84

answers:

5

I am trying to track down a very elusive bug in an application that manipulates a FlowDocument. I have shown below three consecutive lines of debugging code, together with their output:

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

Can anyone help me to understand how two references to the same object can have different values for a given property? Or am I missing something about the way ReferenceEquals works?

Thanks,

Tim

Edit:

If I change the assertion to an if block, the debugging code never runs ...

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

... which makes me feel utterly stupid, because the ReferenceEquals test is clearly working, but I don't understand why the assertion is not working.

+4  A: 

Two things that might be happening from the top of my mind:

  • Accessing Blocks or Blocks.Count might mutate state (it shouldn't, but it is possible).
  • The object might be changed on another thread between the two calls. Do you use multi-threading in the application ?

Also, if the references are of different types (ie. document2 is of an inherited type), the property might be overloaded to return something different. You could check to see whether document1.GetType() == document2.GetType().

Edit in response to your update

Debug.Assert will only ever run, if the assembly is compiled in Debug mode. If you are running Release, it will not be run. This is because Debug.Assert is decorated with the [Conditional("DEBUG")] attribute.

It seems that the issue is the fact that you indeed have 2 different objects.

driis
Yes, you're right - it seems like I have two different objects. I am sorry for wasting your time with an unsubstantiated question.
Tim Coulter
+2  A: 

If a property has side effects it can yield different results each time you call it. E.g. DateTime.Now does not always equal DateTime.Now.

Without knowing anything more about the code, that would be my guess.

EDIT: Using Reflector on FlowDocument shows that Blocks return a new instance each time it is called. Additionally, the Count property BlockCollection is rather elaborate, so I would take a closer look at that. Unfortunately I don't know the involved types very well, so I can't immediately tell you what is wrong.

Brian Rasmussen
Brian - thanks for that insight. I don't think it is the direct cause of my reported problem (my stupidity is responsible for that) but it could have a bearing on the bug I am trying to find. Thanks again.
Tim Coulter
No problem. We all make mistakes like that every now and then. I am glad you got a solution.
Brian Rasmussen
+1  A: 

Are you certain that the Blocks object reference points to the same object? Try a

Debug.Assert(ReferenceEquals(document1.Blocks, document2.Blocks));

and see if that succeeds.

MasterMax1313
A: 

Is the output of the WriteLine directed to document1/document2 by any chance? :).

Freddy Flares
+1  A: 

Possibilities (some of which you have already discounted in the comments):

  1. Some external process, say something that is loading Blocks into FlowDocument, is altering the value between writes.

  2. Heisenberg: reading the Blocks property affects it. This happens sometimes when reading rows from a data source. I'm not familiar with FlowDocument so I'm not sure how feasible this is.

  3. If the instances were declared as different types, their references would still be equal, but the value of Blocks (or Blocks.Count) could be overridden, resulting in different return values since different code might be called - like Object.ToString() vs Int.ToString().

  4. You're somehow calling this debug code in the middle of a loop. This could happen if you're running it in the command window or some attached debugger instead of within the application.

  5. You have dead pixels on your screen that make the first "3" look like a "1".

  6. You live next to a nuclear reactor.

Some things to try:

  1. Run your .Assert code in a loop and see if the values stabilize.

  2. Set a read/write breakpoint on the Blocks value. (I know you can do this in C, but haven't tried it in C#)

Update

Regarding your additional question about .Assert() not working as expected:

Just looked at this note on MSDN regarding Debug.Assert().

By default, the Debug.Assert method works only in debug builds. Use the Trace.Assert method if you want to do assertions in release builds. For more information, see Assertions in Managed Code.

Are you running a debug build or a release build?

David Lively
7. Solar Flares
drharris
Sorry David, it's seems like I misled you (see edit above). It would still be nice to know why the Assert doesn't work though. Your answer 6 is a distinct possibility there. :)
Tim Coulter
I'm running a debug build, but even if I had slipped up here, wouldn't it similarly affect Debug.WriteLine() ?
Tim Coulter
Damn. =/ Not sure.
David Lively
In any case, I'll accept your answer, since you helped me see the flaw in my logic (and I apologize to everyone for a time-wasting question).
Tim Coulter
Thanks. =) Another thought - set a break on the .Assert and trace into it. (I seem to remember MS having a source repository that could be used for tracing into framework code.) Also, this isn't a time waster.
David Lively