前一段时间,接手同事做的一个工具,该工具已经很长时间没有修改过了,前几天有反馈说,工具运行最近有点小问题.便对代码调试跟了一下,看看是哪里的问题.这不是重点,在调试的时候发现记录日志是抛异常的.
先看看这一块有没有问题:
c#文件流释放异常
测试代码:
private static void Main(string[] args)
{
try
{
Log.WriteLog("123.txt", "hello csharp");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
调用记录日志方法,出现异常,提示文件已关闭
说一下,调用文件流的Dispose方法,是没有问题的,那为什么会出现异常呢?是调用StreamWriter.Close方法时报的异常.这里应该这样写.
public static void WriteLog(string fileName, string log)
{
string filePath = Path.Combine(Directory.GetCurrentDirectory(), fileName);
FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
StreamWriter sw = new StreamWriter(fs);
try
{
sw.BaseStream.Seek(0, SeekOrigin.End);
sw.WriteLine(log);
sw.Flush();
}
finally
{
sw.Close(); //先对StreamWriter进行关闭
fs.Dispose(); //再对FileStream进行释放
}
}
接着我们看看StreamWriter的Close源码:
public override void Close()
{
Dispose(true); //调用StreamWriter的Dispose
GC.SuppressFinalize(this);
}
protected override void Dispose(bool disposing)
{
try
{
// We need to flush any buffered data if we are being closed/disposed.
// Also, we never close the handles for stdout & friends. So we can safely
// write any buffered data to those streams even during finalization, which
// is generally the right thing to do.
if (!_disposed && disposing)
{
// Note: flush on the underlying stream can throw (ex., low disk space)
CheckAsyncTaskInProgress();
Flush(flushStream: true, flushEncoder: true); //调用Flush将缓冲区的数据写入到文件流中,如果先调用FileStream的Dispose进行释放,在这里提示文件关闭
}
}
finally
{
CloseStreamFromDispose(disposing);
}
}
private void CloseStreamFromDispose(bool disposing)
{
// Dispose of our resources if this StreamWriter is closable.
if (_closable && !_disposed)
{
try
{
// Attempt to close the stream even if there was an IO error from Flushing.
// Note that Stream.Close() can potentially throw here (may or may not be
// due to the same Flush error). In this case, we still need to ensure
// cleaning up internal resources, hence the finally block.
if (disposing)
{
_stream.Close(); //将缓冲区数据写入到文件流中,会对文件流进行关闭,
//_stream就是在实例化StreamWriter传入的FileStream
}
}
finally
{
_disposed = true;
_charLen = 0;
base.Dispose(disposing);
}
}
}
接着看Flush方法源码:
private void Flush(bool flushStream, bool flushEncoder)
{
// flushEncoder should be true at the end of the file and if
// the user explicitly calls Flush (though not if AutoFlush is true).
// This is required to flush any dangling characters from our UTF-7
// and UTF-8 encoders.
ThrowIfDisposed(); //判断文件流是否关闭,如果关闭抛出异常
// Perf boost for Flush on non-dirty writers.
if (_charPos == 0 && !flushStream && !flushEncoder)
{
return;
}
if (!_haveWrittenPreamble)
{
_haveWrittenPreamble = true;
ReadOnlySpan preamble = _encoding.Preamble;
if (preamble.Length > 0)
{
_stream.Write(preamble);
}
}
// For sufficiently small char data being flushed, try to encode to the stack.
// For anything else, fall back to allocating the byte[] buffer.
scoped Span byteBuffer;
if (_byteBuffer is not null)
{
byteBuffer = _byteBuffer;
}
else
{
int maxBytesForCharPos = _encoding.GetMaxByteCount(_charPos);
byteBuffer = maxBytesForCharPos <= 1024 ? // arbitrary threshold
stackalloc byte[1024] :
(_byteBuffer = new byte[_encoding.GetMaxByteCount(_charBuffer.Length)]);
}
int count = _encoder.GetBytes(new ReadOnlySpan(_charBuffer, 0, _charPos), byteBuffer, flushEncoder);
_charPos = 0;
if (count > 0)
{
_stream.Write(byteBuffer.Slice(0, count));
}
if (flushStream)
{
_stream.Flush();
}
}
图1中代码运行抛出异常,主要是对FileStream释放的时机问题.可以使用简化代码:
public static void WriteLog2(string fileName, string log)
{
string filePath = Path.Combine(Directory.GetCurrentDirectory(), fileName);
using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write))
{
using (StreamWriter sw = new StreamWriter(fs))
{
sw.BaseStream.Seek(0, SeekOrigin.End);
sw.WriteLine(log);
sw.Flush();
}
}
}
其实在很早之前在文章中也提到过文件释放, 具体可以看看: 写更好的CSharp代码
个人能力有限,如果您发现有什么不对,请私信我
如果您觉得对您有用的话,可以点个赞或者加个关注,欢迎大家一起进行技术交流
留言与评论(共有 0 条评论) “” |