在 C# 中使用空 using 语句关闭一次性对象是一种好习惯吗?

Is it good practice to use empty using statement to close a disposable object in C#?

我正在开发一个可以发送fttp请求的class,它有一个实用方法可以执行不同类型的ftp方法:

private FtpWebResponse DoFttpRequest(Uri uri, NetworkCredential credentials, string method, string file = null)
{
    var request = (FtpWebRequest)WebRequest.Create(uri);
    request.Credentials = credentials;
    request.Method = method;

    if (!string.IsNullOrEmpty(file))
    {
        using (var stream = request.GetRequestStream())
        using (var writer = new StreamWriter(stream))
        {
            writer.Write(file);
        }
    }

    return (FtpWebResponse)request.GetResponse();
}

如您所见,此方法执行 ftp 方法和 returns 响应流给调用者。下面是使用此方法将字符串内容写入文件的客户端方法 ftp:

public void WriteToFile(string path, string contents)
{
    var uri = new Uri(path);
    using (var ftpResponse = DoFttpRequest(uri, _credentials, Ftp.UploadFile, contents)) { }
}

如您所见,这里我使用空 using 语句 using (var ftpResponse = DoFttpRequest(uri, _credentials, Ftp.UploadFile, contents)) { } 来处理接收到的流。 这是处理这样的对象的好方法吗?是否有必要处理这个流,因为它可能无论如何都会被垃圾收集器处理掉?

using 语句的作用实际上是执行某种代码,然后简单地调用 Dispose 方法。 这就是为什么你只能使用它的类型继承自 IDisposible 接口(在大多数情况下)

所以你真的不必使用 using 语句。只需调用

DoFttpRequest(uri, _credentials, Ftp.UploadFile, contents)).Dispose()

如果您不自行处置和反对,垃圾收集器 会在范围完成后自动处置它。 当您使用像 c#、java 这样的高级语言时,您不必过多考虑内存问题......它们被称为 内存管理语言。他们会为您处理这些工作人员。

Is it even necessary to dispose this stream, since it will probably be disposed by the garbage collector anyway

您可以使用这个简单的代码来了解不处理响应流可能会如何完全破坏应用程序。我使用 http 请求而不是 ftp 来简化测试,但这同样适用于 ftp 请求。

public class Program {
    static void Main(string[] args) {
        // this value is *already* 2 by default, set for visibility
        ServicePointManager.DefaultConnectionLimit = 2;
        // replace example.com with real site
        DoFttpRequest("http://example.com");
        DoFttpRequest("http://example.com");
        DoFttpRequest("http://example.com");
        Console.ReadLine();
    }

    private static HttpWebResponse DoFttpRequest(string uri) {
        var request = (HttpWebRequest) WebRequest.Create(uri);
        var response = (HttpWebResponse) request.GetResponse();
        Console.WriteLine("got response");
        return response;
    }
}

请注意,您没有处置 HttpWebResponse。将会发生的情况是您将在控制台中看到 2 "got response" 条消息,然后应用程序将挂起以尝试第 3 次获得响应。这是因为每个端点(每个主机)的并发连接限制为 2,因此虽然与主机的 2 个连接(此处为 example.com)为 "in progress" - 与同一主机的下一个连接必须等待它们完成.因为您不处理响应 - 在 GC 收集它们之前,这些连接不会 "completed" 。在那之前 - 你的应用程序挂起然后因超时而失败(如果 request.Timeout 设置为某个合理的时间)。所有后续请求也会挂起,然后因超时而失败。如果您处理响应 - 应用程序将按预期工作。

所以总是处理一次性的东西。使用块不是必需的,你可以只做 DoFtpRequest(..).Dispose()。但是如果你更喜欢空使用——至少不要声明不必要的变量,只要做using (DoFttpRequest(..)) {}。在 empty using 和 Dispose 之间进行选择时要注意的一件事是 null 被 DoFtpRequest 编辑的可能性 return,因为如果它将 return null - 显式 Dispose 将抛出 NullReferenceException 而空 using 将忽略它(如果你期望空值但不想使用 using,你可以 DoFttpRequest(...)?.Dispose();)。