1

I'm developing a class that can send fttp requests, it has a utility method that can execute different types of ftp methods:

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();
}

As you can see, this methods executes ftp method and returns response stream to the caller. Here is the client method that uses this method to write string contents to a file through ftp:

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

As you can see, here I'm using empty using statement using (var ftpResponse = DoFttpRequest(uri, _credentials, Ftp.UploadFile, contents)) { } to dispose of the received stream. Is this a good approach to dispose object like that? Is it even necessary to dispose this stream, since it will probably be disposed by the garbage collector anyway?

Mykhailo Seniutovych
  • 3,527
  • 4
  • 28
  • 50
  • 1
    Why do you need a reference to it anyway if you are not going to use it? I would simply write `DoFttpRequest(new Uri(path), _credentials, Ftp.UploadFile, contents)` – Zohar Peled Nov 02 '17 at 08:00
  • 3
    It is necessary to dispose it. Instead of using you can just do `DoFttpRequest(..).Dispose()`. If you use empty using - no need to declare variable: `using (DoFtpRequest(...)) {}`. – Evk Nov 02 '17 at 08:03
  • It seems to me that the question has nothing to do with `ftp` or `webrequest`, it's more about `using` in general – Rafalon Nov 02 '17 at 08:09
  • using = try { whatever } finally { dispose }. Use using... but use it with { } so it's clear what's happening. – Marco Nov 02 '17 at 08:10
  • @Marco it is a common convention that if you have several usings linked you only use one set of brackets. – default Nov 02 '17 at 08:19
  • It just isn't "empty". C# does not require using { braces } when you write a single statement. And does not insist that you indent that statement. Make your code readable to yourself by adding those braces. – Hans Passant Nov 02 '17 at 08:41

2 Answers2

5

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

You can use this simple code to see how not disposing response stream might completely break application. I use http request instead of ftp for simlicity of testing, but that applies equally to ftp requests.

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;
    }
}

Note that you are not disposing HttpWebResponse. What will happen is you will see 2 "got response" messages in console and then application will hang trying to get response 3rd time. That's because concurrent connections limit per endpoint (per host) is 2, so while 2 connections to the host (example.com here) are "in progress" - next connection to the same host will have to wait for them to complete. Because you don't dispose response - those connections will not be "completed" until GC collects them. Until then - your application hangs and then fails by timeout (if request.Timeout is set to some reasonable time). All subsequent requests also hang then fail by timeout. If you dispose responses - application will work as expected.

So always dispose things that are disposable. Using block is not necessary, you can just do DoFtpRequest(..).Dispose(). But if you prefer empty using - at least don't declare unnecessary variable, just do using (DoFttpRequest(..)) {}. One thing to note when choosing between empty using and Dispose is the possibility of null being returned by DoFtpRequest, because if it will return null - explicit Dispose will throw NullReferenceException while empty using will just ignore it (you can do DoFttpRequest(...)?.Dispose(); if you expect nulls but don't want to use using).

Evk
  • 98,527
  • 8
  • 141
  • 191
  • 1
    Calling `Dispose()` will not happen if an exception is thrown. Unless you wrap your call into a try-finally block and call it there. And that is excactly what a using block does for you. – thehennyy Nov 02 '17 at 08:21
  • 2
    @thehennyy but here using block is empty. If exception happens inside `DoFttpRequest` - there is nothing to dispose, no value is yet returned. When we have value returned - we immediately dispose it. – Evk Nov 02 '17 at 08:24
-2

What using statement does is actually execute some sort of code and then simply calls the Dispose method. That's why u can use it only types inherits from IDisposible interface(in most case)

So you don't really have to use using statement. Just simply call

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

If you dont Dispose and object by yourself the Garbage Collector automatically disposes it after the scope completed. You don't have to think much about memory when you use high level languages like c#, java ... They are called Memory Managed Languages. They handle thoose kind of staff for you.

DLL_Whisperer
  • 815
  • 9
  • 22
  • 1
    Not really. If the method throws an exception it won't be disposed. That's why he should use "using" – Marco Nov 02 '17 at 08:09
  • "Garbage Collector automatically disposes it after the scope completed" Not true, GC can only handle managed ressources. Implementing `IDisposable` makes only sense for *unmanaged* ressources. You have to call `Dispose`, GC won´t do that for you. – MakePeaceGreatAgain Nov 02 '17 at 08:13
  • if you use using statement and you kill the process it won't be disposed either. You must be handling the exceptions. And question is not about exceptions :) or i can simply modify my answer with calling dispose method in finally block... But like i say if you kill the process finally block wont be called – DLL_Whisperer Nov 02 '17 at 08:13
  • 1
    "But like i say if you kill the process finally block wont be called" True, but if you kill it in the moment you try to call `Dispose` on your own it´ll surely won´t be disposed either. This has nothing to do with if you´re calling `Dipose` self or if you´re using `using`-statement. Killing a process however isn´t part of that question IMO. – MakePeaceGreatAgain Nov 02 '17 at 08:18
  • @HimBromBeere After scope completed the reference will be removed. And when there is no reference to that object, gc will take care of the object https://stackoverflow.com/a/1480897/2711861 – DLL_Whisperer Nov 02 '17 at 08:21
  • 1
    But GC won´t call `Dispose`. That´s the whole point of `using`-statement. See https://stackoverflow.com/a/45049/2528063 – MakePeaceGreatAgain Nov 02 '17 at 08:25
  • 1
    If the method throws an exception it won't return anything, hence nothing to dispose. The thing that needs to be handled, however, is that the method might return `null`, which `using` would handle, so the correct call would be `?.Dispose()`. Other than that it is fine. – Lasse V. Karlsen Nov 02 '17 at 08:41