views:

375

answers:

7

I'm making a method to fetch a list of filenames from a server but I have come to a problem that I cannot answer.

The method returns two things:

  • an SftpResult which is an enum with a variety of return codes.
  • the list of filenames.

Of these three signatures:

public static ArrayList GetFileList(string directory, out SftpResult result)

public static SftpResult GetFileList(string directory, out ArrayList fileNames)

public static SftpFileListResult GetFileList(string directory)

(where SftpFileListResult is a composite object of an SftpResult and an ArrayList)

which is preferred and why?

+4  A: 

I prefer the final option. Out parameters are rarely used and can be surprising / confusing to some. Creating a composite result object is a clean solution. The only drawback is that you must create a class solely for this purpose.

Mark Byers
".. rarely used and can be surprising .. " - Try telling that to a C or C++ programmer!
Peter M
+28  A: 

Personally I prefer the last option (although using a List<T> or ReadOnlyCollection<T> instead of ArrayList). out parameters are basically a way of returning multiple values, and it's generally nicer to encapsulate those.

Another option in .NET 4 would be

Tuple<SftpResult, ArrayList> GetFileList(string directory)

That explicitly says, "this method returns two things... I've packed them together for you for this particular case, but it's not worth further encapsulating them: they're not worth composing in a separate type".

(If you're not using .NET 4 you could always write your own Tuple type.)

Jon Skeet
Thanks, I didn't even consider a Tuple. That was my main problem with the third option: I felt like it didnt really warrant a whole new class.
rmx
+1 also for tuples, probably one of my most favorite things about .NET 4.
BoltClock
Can't count how many custom Tuple classes I've created over the years - glad there's finally a real one! Now if I could only get our project to update from 3.4 to 4...
mikemanne
Man, I want Tuple. :( Never used one before but it looks quite useful.
Dave
+7  A: 

I would prefer to wrap it up in a return object:

class FileResult
{
    public FileResult(SftpResult resultCode, IEnumerable<string> files)
    {
         ResultCode = resultCode;
         FileList = new List<string>(files);
    }
    public SftpResult ResultCode { get; private set; }
    public IEnumerable<string> FileList { get; private set; }
}

It feels a lot cleaner not to use out.

Fredrik Mörk
I think that's the last option presented... although I'd personally make it immutable.
Jon Skeet
@Jon: Yes, it's the last option, and of course it should be immutable. I don't know what's wrong with me; should not be here today ;)
Fredrik Mörk
+2  A: 

I would say the third, since it encapsulates the logic that you need in one location. I can only assume that SftpResult and ArrayList return methods should be private and then make up the internal logic of the composite return object.

Woot4Moo
+2  A: 

I would make it:

public static bool GetFileList(string directory, out SftpResult result, out ArrayList fileNames)

So there is no confusion about what the function does, and also i would return a bool, if the GetFileList can fail.

Marcus Johansson
+1  A: 

Why not provide all three? I prefer not having out parameters in my function calls but I can appreciate that in many times they're needed. If you don't know which signature to support then support all three. Ultimately, the choice is on the side of the caller and the best thing you can do is offer this choice.

DeadMG
In a word .. YAGNI .. http://en.wikipedia.org/wiki/You_ain't_gonna_need_it
Peter M
+2  A: 

If you like designs where one function does two things then I would use a tuple a la Jon or a return object a la Fredrik.

If you wanted to be all OOPy about it you could let the type system do the work:

abstract class FtpResult { ... }
sealed class FileList : FtpResult { ... }
sealed class Error : FtpResult { ... }
...
sealed class FtpService
{
    ...
    public FtpResult GetFileList(string directory) { ... }
    ...
}
...
var result = service.GetFileList(dir);
var error = result as Error;
var list = result as FileList;
if (error != null) { ... }
else if (list != null)
{
    foreach(var name in list.Files) { ... }
}
... 
Eric Lippert