Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Patterns for simulating optional "out" parameters in C#?

I'm translating an API from C to C#, and one of the functions allocates a number of related objects, some of which are optional. The C version accepts several pointer parameters which are used to return integer handles to the objects, and the caller can pass NULL for some of the pointers to avoid allocating those objects:

void initialize(int *mainObjPtr, int *subObjPtr, int *anotherSubObjPtr);

initialize(&mainObj, &subObj, NULL);

For the C# version, the obvious translation would use out parameters instead of pointers:

public static void Initialize(out int mainObj, out int subObj,
    out int anotherSubObj);

... but this leaves no way to indicate which objects are unwanted. Are there any well-known examples of C# APIs doing something similar that I could imitate? If not, any suggestions?

like image 300
Jesse McGrew Avatar asked Sep 01 '26 20:09

Jesse McGrew


2 Answers

Well, you shouldn't be using int for the objects anyway - they should be references, i.e. out SomeMainType mainObj, out SomSubType subObj. That done, you can then use overloads, but this would be ungainly.

A better approach would be to return something with the 3 objects - a custom type, or in .NET 4.0 maybe a tuple.

Something like:

class InitializeResult {
    public SomeMainType MainObject {get;set;}
    public SomeSubType SubObject {get;set;}
    ...
}
public static InitializeResult Initialize() {...}

Re-reading it, it looks like the caller is also passing data in (even if only null / not-null), so out was never the right option. Maybe a flags enum?

[Flags]
public enum InitializeOptions {
    None = 0, Main = 1, Sub = 2, Foo = 4, Bar = 8, ..
}

and call:

var result = Initialize(InitializeOptions.Main | InitializeOptions.Sub);
var main = result.MainObject;
var sub = result.SubObject;
like image 79
Marc Gravell Avatar answered Sep 03 '26 11:09

Marc Gravell


The closest translation would be using ref and IntPtr

public static void Initialize(ref IntPtr mainObj, ref IntPtr subObj,
ref IntPtr anotherSubObj)

and specifying IntPtr.Zero for unwanted values.

But for me the question arises why you want to resemble the API that close unless you are trying to figure out a P/Invoke signature. Assuming mainObj has accessible references to both sub object something like

public static MainObjectType Initialize(bool initSubObj, bool initAnotherSubObj)

appears to be a much cleaner solution to me. In .NET 4 you can even make the boolean arguments optional or simulate this with overloads in pre .NET 4. If there are no accessible references to the sub objects you could return a simple container type holding the references.

like image 38
Daniel Brückner Avatar answered Sep 03 '26 09:09

Daniel Brückner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!