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

Myk*_*ych 1 c# ftp dispose using webrequest

我正在开发一个可以发送fttp请求的类,它有一个可以执行不同类型的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();
}
Run Code Online (Sandbox Code Playgroud)

如您所见,此方法执行ftp方法并将响应流返回给调用者.以下是使用此方法通过ftp将字符串内容写入文件的客户端方法:

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

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

Evk*_*Evk 5

甚至有必要处理这个流,因为无论如何它可能会被垃圾收集器处理掉

您可以使用这个简单的代码来查看如何不处理响应流可能会完全破坏应用程序.我使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

所以总是处理一次性的东西.使用块不是必需的,你可以这样做DoFtpRequest(..).Dispose().但如果您更喜欢空闲使用 - 至少不要声明不必要的变量,那就行了using (DoFttpRequest(..)) {}.在使用empty之间选择时要注意的一点Dispose是,返回null的可能性是DoFtpRequest,因为如果它将返回null - 显式Dispose将抛出NullReferenceException而空使用将只是忽略它(你可以做DoFttpRequest(...)?.Dispose();如果你期望空值但不想要使用).

  • @thehennyy但是这里使用块是空的.如果在`DoFttpRequest`中发生异常 - 没有什么可以处理的,还没有返回任何值.当我们返回价值时 - 我们会立即处理它. (2认同)