ObjectDisposedException when outputting to console

2019-06-08 15:40发布

问题:

If I have the following code, I have no runtime or compilation problems:

if (ConsoleAppBase.NORMAL_EXIT_CODE == code)
{
    StdOut.WriteLine(msg);
}
else
{
    StdErr.WriteLine(msg);
}

However, in trying to make this more concise, I switched to the following code:

(ConsoleAppBase.NORMAL_EXIT_CODE == code
    ? StdOut
    : StdErr
).WriteLine(msg);

When I have this code, I get the following exception at runtime:

System.ObjectDisposedException: Cannot write to a closed TextWriter

Can you explain why this happens? Can I avoid it and have more concise code like I wanted?

Edit: whoops, sorry, I forgot to note where these mysterious StdOut and StdErr come from:

/// <summary>
/// Define these so that they can be accessible to unit tests so that
/// a different TextWriter instance can be used for capturing output
/// for verification.
/// </summary>
internal static TextWriter StdOut = Console.Out;
internal static TextWriter StdErr = Console.Error;

Update: hm, I just got the same exception with the original, lengthy code, so apparently something else is awry. I'll check what my test cases are doing.

Update again: turns out in my tests I was rerouting standard out but not standard error, but then I did try to write to standard error and it wigged out. My fix:

var standardOut = new StreamWriter(Console.OpenStandardOutput())
                      {
                          AutoFlush = true
                      };
Console.SetOut(standardOut);

// Added this:
var standardError = new StreamWriter(Console.OpenStandardError())
                        {
                            AutoFlush = true
                        };
Console.SetError(standardError);

I'm marking ChaosPandion's answer as the correct one since he correctly identified my test as being screwy.

回答1:

The only explanation is that the circumstances of your test are different. For instance, in test one you are writing StdOut but in test two you are writing to StdErr because of some sort of error. Open up the debugger and make sure you aren't disposing of either before executing this code.



回答2:

I believe the method you're looking for is:

Console.WriteLine(msg);

And

Console.Error.WriteLine(msg);

Edit:

I believe to use Console.Out and Console.Error you may have to set them with the SetError and SetOut methods actually.

Instead you can try to call these functions:

Console.OpenStandardError();
Console.OpenStandardOutput();

Which means you may have to make your own writers, and manage closing the stream since these return System.IO.Stream objects.

So:

TextWriter StdOut = new TextWriter(Console.OpenStandardOutput());
TextWriter StdErr = new TextWriter(Console.OpenStandardError());

I just use the Console.Write / Console.WriteLine in my coding. Also Debug.Write and Debug.WriteLine for the Error Stream.