在C#中,使用“using”会导致错误,有比不使用“using”更好的实践吗?

huangapple go评论134阅读模式
英文:

In C#, using "using" results in errors, is there a better practice than just not using "using"?

问题

以下是您提供的方法的代码部分的翻译,不包括任何问题回答:

[SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "If we dispose of the csvWriter, it won't be available to write.")]
public static MemoryStream CreateCsvStream(IEnumerable<object> records)
{
    MemoryStream memoryStream = new();
    StreamWriter streamWriter = new(memoryStream);
    CsvWriter csvWriter = new(streamWriter, CultureInfo.InvariantCulture);
    csvWriter.Context.TypeConverterCache.AddConverter<bool>(new CollendaBooleanConverter());
    csvWriter.Context.TypeConverterCache.AddConverter<bool?>(new CollendaBooleanConverter());
    csvWriter.Context.TypeConverterCache.AddConverter<DateOnly>(new CollendaDateOnlyConverter());
    csvWriter.Context.TypeConverterCache.AddConverter<DateOnly?>(new CollendaDateOnlyConverter());
    csvWriter.Context.TypeConverterCache.AddConverter<decimal>(new CollendaDecimalConverter());
    csvWriter.Context.TypeConverterCache.AddConverter<decimal?>(new CollendaDecimalConverter());
    csvWriter.WriteRecords(records);
    streamWriter.Flush();
    return memoryStream;
}
private void Upload(MemoryStream memoryStream)
{
    _sftpClient.Connect();
    _ = memoryStream.Seek(0, SeekOrigin.Begin);

    string filePath = _collendaSftpConfig?.FilePath
        ?.InsertTimestamp()
        ?? throw new InvalidOperationException("CollendaSftpConfig configuration is missing FilePath.");

    _sftpClient.UploadFile(memoryStream, filePath);
    _sftpClient.Disconnect();
}

希望这能帮助您。如果您有任何其他问题,可以随时提出。

英文:

I have the following method:

	[SuppressMessage(&quot;Reliability&quot;, &quot;CA2000:Dispose objects before losing scope&quot;, Justification = &quot;If we dispose of the csvWriter, it won&#39;t be available to write.&quot;)]
	public static MemoryStream CreateCsvStream(IEnumerable&lt;object&gt; records)
	{
		MemoryStream memoryStream = new();
		StreamWriter streamWriter = new(memoryStream);
		CsvWriter csvWriter = new(streamWriter, CultureInfo.InvariantCulture);
		csvWriter.Context.TypeConverterCache.AddConverter&lt;bool&gt;(new CollendaBooleanConverter());
		csvWriter.Context.TypeConverterCache.AddConverter&lt;bool?&gt;(new CollendaBooleanConverter());
		csvWriter.Context.TypeConverterCache.AddConverter&lt;DateOnly&gt;(new CollendaDateOnlyConverter());
		csvWriter.Context.TypeConverterCache.AddConverter&lt;DateOnly?&gt;(new CollendaDateOnlyConverter());
		csvWriter.Context.TypeConverterCache.AddConverter&lt;decimal&gt;(new CollendaDecimalConverter());
		csvWriter.Context.TypeConverterCache.AddConverter&lt;decimal?&gt;(new CollendaDecimalConverter());
		csvWriter.WriteRecords(records);
		streamWriter.Flush();
		return memoryStream;
	}

This works, but as hinted in the SuppressMessage, if I use using so MemoryStream, StreamWriter, and/or CsvWriter are disposed of, when I later execute the following code:

	private void Upload(MemoryStream memoryStream)
	{
		_sftpClient.Connect();
		_ = memoryStream.Seek(0, SeekOrigin.Begin);

		string filePath = _collendaSftpConfig?.FilePath
			?.InsertTimestamp()
			?? throw new InvalidOperationException(&quot;CollendaSftpConfig configuration is missing FilePath.&quot;);

		// Renci.SshNet&#39;s Sftp Client seems to have some async support, but it seems much more complicated to consume.
		// There is no clear benefit to using it at this time.
		_sftpClient.UploadFile(memoryStream, filePath);
		_sftpClient.Disconnect();
	}

I'll get an error like:

> System.ObjectDisposedException
HResult=0x80131622
Message=Cannot access a closed Stream.
Source=System.Private.CoreLib
StackTrace:
at System.ThrowHelper.ThrowObjectDisposedException_StreamClosed(String objectName)
at System.IO.MemoryStream.Seek(Int64 offset, SeekOrigin loc)
at Enpal.Collenda.Client.SftpUploader.CollendaSftpClient.Upload(MemoryStream memoryStream) in C:\projects\FinTech\collenda-finance-listener\Collenda.Client.SftpUploader\CollendaSftpClient.cs:line 59

I did not experiment with every permutation of adding or not adding using to each of these, but I did try adding to all and some of them and received similar results.

Of course, the simple "solution" is just to leave them out and suppress this, but I'm concerned whether this is a reliable solution and whether there may be a better way to handle this situation (and potentially similiar situations in the future).

答案1

得分: 12

StreamWriter 默认情况下在 Dispose 时关闭底层流,您可以使用接受 leaveOpen 参数 的构造函数,并将其设置为 true

> leaveOpen 布尔值
> true 表示在 StreamWriter 对象被释放后保持流开放;否则为 false

MemoryStream memoryStream = new();
using StreamWriter streamWriter = new(memoryStream, leaveOpen: true);

此外,将 StreamWriter 包装在 using 中时,不再需要调用 Flush - 它将在 Dispose 时自动发生。

英文:

StreamWriter by default closes underlying stream on Dispose, you can use constructor accepting leaveOpen parameter and set it to true:

> leaveOpen Boolean
> true to leave the stream open after the StreamWriter object is disposed; otherwise, false.

MemoryStream memoryStream = new();
using StreamWriter streamWriter = new(memoryStream, leaveOpen: true);

Also when wrapping the StreamWriter into using there is no more need to call Flush - it will happen automatically on Dispose.

答案2

得分: 12

以下是代码的翻译部分:

public static MemoryStream CreateCsvStream(IEnumerable<object> records)
{
    // 这里没有使用using语句,因为我们希望流保持打开状态
    MemoryStream memoryStream = new();

    // 我们希望释放StreamWriter
    using StreamWriter streamWriter = new(memoryStream, leaveOpen: true);

    // 我假设CsvWriter也实现了IDisposable
    using CsvWriter csvWriter = new(streamWriter, CultureInfo.InvariantCulture);

    // 在这里写入csvWriter
    ...

    // 不需要刷新StreamWriter - 它在被释放时会自动发生
    return memoryStream;
}
英文:

You're trying to read from the stream afterwards, and that's all. In that case, you don't want to dispose of the stream (because you still want to be able to read) but probably you do want to dispose of the StreamWriter. (It won't actually matter with MemoryStream, but I can understand wanting to do it.)

The simplest option here is to use the StreamWriter constructor that allows you to suppress it closing the underlying stream:

public static MemoryStream CreateCsvStream(IEnumerable&lt;object&gt; records)
{
    // No using statement here, because we want the stream to stay open
    MemoryStream memoryStream = new();

    // We *do* want to dispose of the StreamWriter
    using StreamWriter streamWriter = new(memoryStream, leaveOpen: true);

    // I assume that CsvWriter implements IDisposable too
    using CsvWriter csvWriter = new(streamWriter, CultureInfo.InvariantCulture);

    // Write to csvWriter here
    ...

    // No need to flush the StreamWriter - that&#39;ll happen when it&#39;s disposed
    return memoryStream;
}

huangapple
  • 本文由 发表于 2023年3月3日 23:38:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/75629101.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定