C# 4.0 optional out/ref arguments
Asked Answered
S

11

251

Does C# 4.0 allow optional out or ref arguments?

Socialist answered 20/5, 2010 at 1:45 Comment(7)
Weell, C++ effectively has them for "out" parameters - you can have an address argument initialised to null and it's quite common to write library code that will only populate such a return structure if the pointer is non-null. That's an idiom going back to using null for "optional arguments" in C APIs.Swithbert
@Ed and everyone: why would this make no sense? If a function "returns" a value via "out", I don't want to be forced to accept it. Now I know that for technical reasons the compiler still has to pass something in, but there's no reason why it couldn't just create a dummy local for me behind my back.Eckart
Maybe it makes no sense from the standpoint of how things are implemented or what an optional parameter actually is. But like romkyns said, it would be really nice to have "optional out arguments" - parse that in English rather than CLR and it becomes reasonable and in IMO desirable.Selfanalysis
C# doesn't, but VB.NET does.Mccrae
This has been beaten to death, however, I can't help but mention my support for optional out arguments. I've become fairly accustomed to optional arguments by reference via setting a null default (I come from PHP) and testing for null to proceed with populating the argument (for those familiar, think preg_match()) Anyway, while I understand from a technical point this may currently be impossible, and that PHP and C# are rather incomparable, it would still be a "nice" tool to have available.Rowlett
Just use an overloaded method that does not take the out parameter in question, if you have a few of them you want optional though this could get messy.Chessman
devblogs.microsoft.com/dotnet/whats-new-in-csharp-7-0/… was saying "We plan to allow “wildcards” as out parameters as well, in the form of a *, to let you ignore out parameters you don’t care about: p.GetCoordinates(out int x, out *); // I only care about x --- Note: It is still uncertain whether wildcards make it into C# 7.0 - However at devblogs.microsoft.com/dotnet/new-features-in-c-7-0 I don't see mention of wildcards, so probably they didn't keep that featureArundell
E
99

As already mentioned, this is simply not allowed and I think it makes a very good sense. However, to add some more details, here is a quote from the C# 4.0 Specification, section 21.1:

Formal parameters of constructors, methods, indexers and delegate types can be declared optional:

fixed-parameter:
    attributesopt parameter-modifieropt type identifier default-argumentopt
default-argument:
    = expression

  • A fixed-parameter with a default-argument is an optional parameter, whereas a fixed-parameter without a default-argument is a required parameter.
  • A required parameter cannot appear after an optional parameter in a formal-parameter-list.
  • A ref or out parameter cannot have a default-argument.
Ennoble answered 20/5, 2010 at 2:6 Comment(2)
Alternatively, you can create an overload with a ref/out parameter. Yes, you'll have two function definitions, but it will accomplish what you're afterVertical
Not if you have 10 optional ref parameters. Then you have a giant mess when trying to port to C#Rucksack
G
264

No.

A workaround is to overload with another method that doesn't have out / ref parameters, and which just calls your current method.

public bool SomeMethod(out string input)
{
    ...
}

// new overload
public bool SomeMethod()
{
    string temp;
    return SomeMethod(out temp);
}

If you have C# 7.0, you can simplify:

// new overload
public bool SomeMethod()
{
    return SomeMethod(out _);    // declare out as an inline discard variable
}

(Thanks @Oskar / @Reiner for pointing this out.)

Gaven answered 30/11, 2011 at 6:26 Comment(8)
any idea for more elegant solution than declaring temp/dummy?Outmarch
What's not elegant about that? Looks perfectly decent to me.Defecate
Maybe decent, but elegant is definitely on another league. The fact there's no better solution doesn't mean this is state-of-the-art by decree.Jodijodie
Hold on @o0'. In C# 7.0 you will be able to do this: return SomeMethod(out string temp). See more here: blogs.msdn.microsoft.com/dotnet/2016/08/24/…Alfonzoalford
In C# 7.0 you can use a temporary write-only variable like: return SomeMethod(out _);Orchardist
If you have multiple optional out parameters, you still end up with a cartesian product set of overloads.Rucksack
@Rucksack True - I'd maybe consider an alternative approach if you need multiple optional outs, or as you say you'd end up with a potentially confusing list of overloads, plus you can't have two overloads with the same signature. Maybe introduce a parameter object or return a response object?Gaven
@Gaven With complex HR software, when creating certain events, we have to optionally return one or more states depending on where the function is called from. I think our most complex one has about 10 optional refs. Most of the time they aren't used, or we could look up the data after the fact, but that would be extra calls to the database. We also have C# modules that call the foundation code in VB.net. When that happens, the C# code ends up looking like a disaster took place with all the unused temp variables.Rucksack
E
99

As already mentioned, this is simply not allowed and I think it makes a very good sense. However, to add some more details, here is a quote from the C# 4.0 Specification, section 21.1:

Formal parameters of constructors, methods, indexers and delegate types can be declared optional:

fixed-parameter:
    attributesopt parameter-modifieropt type identifier default-argumentopt
default-argument:
    = expression

  • A fixed-parameter with a default-argument is an optional parameter, whereas a fixed-parameter without a default-argument is a required parameter.
  • A required parameter cannot appear after an optional parameter in a formal-parameter-list.
  • A ref or out parameter cannot have a default-argument.
Ennoble answered 20/5, 2010 at 2:6 Comment(2)
Alternatively, you can create an overload with a ref/out parameter. Yes, you'll have two function definitions, but it will accomplish what you're afterVertical
Not if you have 10 optional ref parameters. Then you have a giant mess when trying to port to C#Rucksack
P
71

No, but another great alternative is having the method use a generic template class for optional parameters as follows:

public class OptionalOut<Type>
{
    public Type Result { get; set; }
}

Then you can use it as follows:

public string foo(string value, OptionalOut<int> outResult = null)
{
    // .. do something

    if (outResult != null) {
        outResult.Result = 100;
    }

    return value;
}

public void bar ()
{
    string str = "bar";

    string result;
    OptionalOut<int> optional = new OptionalOut<int> ();

    // example: call without the optional out parameter
    result = foo (str);
    Console.WriteLine ("Output was {0} with no optional value used", result);

    // example: call it with optional parameter
    result = foo (str, optional);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional.Result);

    // example: call it with named optional parameter
    foo (str, outResult: optional);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional.Result);
}
Platino answered 24/2, 2014 at 15:6 Comment(6)
It's a very reasonable solution, but one thing to be aware of is that the compiler won't enforce the requirement that the out parameter be assigned before exiting the method.Bangs
I like it, but if you don't want to create a new class, you can simulate it by passing in a single element array.Indebted
Can someone please elaborate on this: "the compiler won't enforce the requirement that the out parameter be assigned before exiting the method"?Dubitable
I assume they meant that if outResult was not set to any value the the compiler would not complain. In other words, it might be easy to forget to set an out variable accidentally.Platino
If the compiler complained then it would not be optional.Stannite
I like this approach more than having to use discards like in this example: var canParse = int.TryParse("123", out _);. Your approach is truly optional. Great!Ricks
S
34

There actually is a way to do this that is allowed by C#. This gets back to C++, and rather violates the nice Object-Oriented structure of C#.

USE THIS METHOD WITH CAUTION!

Here's the way you declare and write your function with an optional parameter:

unsafe public void OptionalOutParameter(int* pOutParam = null)
{
    int lInteger = 5;
    // If the parameter is NULL, the caller doesn't care about this value.
    if (pOutParam != null) 
    { 
        // If it isn't null, the caller has provided the address of an integer.
        *pOutParam = lInteger; // Dereference the pointer and assign the return value.
    }
}

Then call the function like this:

unsafe { OptionalOutParameter(); } // does nothing
int MyInteger = 0;
unsafe { OptionalOutParameter(&MyInteger); } // pass in the address of MyInteger.

In order to get this to compile, you will need to enable unsafe code in the project options. This is a really hacky solution that usually shouldn't be used, but if you for some strange, arcane, mysterious, management-inspired decision, REALLY need an optional out parameter in C#, then this will allow you to do just that.

Sceptic answered 8/5, 2014 at 22:32 Comment(0)
S
8

ICYMI: Included on the new features for C# 7.0 enumerated here, "discards" is now allowed as out parameters in the form of a _, to let you ignore out parameters you don’t care about:

p.GetCoordinates(out var x, out _); // I only care about x

P.S. if you're also confused with the part "out var x", read the new feature about "Out Variables" on the link as well.

Sammiesammons answered 10/3, 2017 at 8:8 Comment(4)
is it _ or * "p.GetCoordinates(out int x, out *); // I only care about x"Wadlinger
looks like I was looking an older version of the document.Wadlinger
Discards aren't really optional variables. It is only for a bit of syntactic sugar at the calling code level. The function itself has no knowledge it has been called with a discard.Stannite
Why can't the discard be automatic like it is in VB.net?Rucksack
R
5

No, but you can use a delegate (e.g. Action) as an alternative.

Inspired in part by Robin R's answer when facing a situation where I thought I wanted an optional out parameter, I instead used an Action delegate. I've borrowed his example code to modify for use of Action<int> in order to show the differences and similarities:

public string foo(string value, Action<int> outResult = null)
{
    // .. do something

    outResult?.Invoke(100);

    return value;
}

public void bar ()
{
    string str = "bar";

    string result;
    int optional = 0;

    // example: call without the optional out parameter
    result = foo (str);
    Console.WriteLine ("Output was {0} with no optional value used", result);

    // example: call it with optional parameter
    result = foo (str, x => optional = x);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional);

    // example: call it with named optional parameter
    foo (str, outResult: x => optional = x);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional);
}

This has the advantage that the optional variable appears in the source as a normal int (the compiler wraps it in a closure class, rather than us wrapping it explicitly in a user-defined class).

The variable needs explicit initialisation because the compiler cannot assume that the Action will be called before the function call exits.

It's not suitable for all use cases, but worked well for my real use case (a function that provides data for a unit test, and where a new unit test needed access to some internal state not present in the return value).

Robinette answered 17/11, 2017 at 9:33 Comment(0)
O
0

Use an overloaded method without the out parameter to call the one with the out parameter for C# 6.0 and lower. I'm not sure why a C# 7.0 for .NET Core is even the correct answer for this thread when it was specifically asked if C# 4.0 can have an optional out parameter. The answer is NO!

Oblation answered 5/4, 2019 at 15:41 Comment(0)
Q
0

For simple types you can do this using unsafe code, though it's not idiomatic nor recommended. Like so:

// unsafe since remainder can point anywhere
// and we can do arbitrary pointer manipulation
public unsafe int Divide( int x, int y, int* remainder = null ) {
    if( null != remainder ) *remainder = x % y;
    return x / y;
}

That said, there's no theoretical reason C# couldn't eventually allow something like the above with safe code, such as this below:

// safe because remainder must point to a valid int or to nothing
// and we cannot do arbitrary pointer manipulation
public int Divide( int x, int y, out? int remainder = null ) {
    if( null != remainder ) *remainder = x % y;
    return x / y;
}

Things could get interesting though:

// remainder is an optional output parameter
// (to a nullable reference type)
public int Divide( int x, int y, out? object? remainder = null ) {
    if( null != remainder ) *remainder = 0 != y ? x % y : null;
    return x / y;
}
Quadrillion answered 8/12, 2021 at 21:43 Comment(0)
K
0

The direct question has been answered in other well-upvoted answers, but sometimes it pays to consider other approaches based on what you're trying to achieve.

If you're wanting an optional parameter to allow the caller to possibly request extra data from your method on which to base some decision, an alternative design is to move that decision logic into your method and allow the caller to optionally pass a value for that decision criteria in. For example, here is a method which determines the compass point of a vector, in which we might want to pass back the magnitude of the vector so that the caller can potentially decide if some minimum threshold should be reached before the compass-point judgement is far enough away from the origin and therefore unequivocally valid:

public enum Quadrant {
    North,
    East,
    South,
    West
}

// INVALID CODE WITH MADE-UP USAGE PATTERN OF "OPTIONAL" OUT PARAMETER
public Quadrant GetJoystickQuadrant([optional] out magnitude)
{
    Vector2 pos = GetJoystickPositionXY();
    float azimuth = Mathf.Atan2(pos.y, pos.x) * 180.0f / Mathf.PI;
    Quadrant q;
    if (azimuth > -45.0f && azimuth <= 45.0f) q = Quadrant.East;
    else if (azimuth > 45.0f && azimuth <= 135.0f) q = Quadrant.North;
    else if (azimuth > -135.0f && azimuth <= -45.0f) q = Quadrant.South;
    else q = Quadrant.West;
    if ([optonal.isPresent(magnitude)]) magnitude = pos.Length();
    return q;
}

In this case we could move that "minimum magnitude" logic into the method and end-up with a much cleaner implementation, especially because calculating the magnitude involves a square-root so is computationally inefficient if all we want to do is a comparison of magnitudes, since we can do that with squared values:

public enum Quadrant {
    None, // Too close to origin to judge.
    North,
    East,
    South,
    West
}

public Quadrant GetJoystickQuadrant(float minimumMagnitude = 0.33f)
{
    Vector2 pos = GetJoystickPosition();
    if (minimumMagnitude > 0.0f && pos.LengthSquared() < minimumMagnitude * minimumMagnitude)
    {
        return Quadrant.None;
    }
    float azimuth = Mathf.Atan2(pos.y, pos.x) * 180.0f / Mathf.PI;
    if (azimuth > -45.0f && azimuth <= 45.0f) return Quadrant.East;
    else if (azimuth > 45.0f && azimuth <= 135.0f) return Quadrant.North;
    else if (azimuth > -135.0f && azimuth <= -45.0f) return Quadrant.South;
    return Quadrant.West;
}

Of course, that might not always be viable. Since other answers mention C# 7.0, if instead what you're really doing is returning two values and allowing the caller to optionally ignore one, idiomatic C# would be to return a tuple of the two values, and use C# 7.0's Tuples with positional initializers and the _ "discard" parameter:

public (Quadrant, float) GetJoystickQuadrantAndMagnitude()
{
    Vector2 pos = GetJoystickPositionXY();
    float azimuth = Mathf.Atan2(pos.y, pos.x) * 180.0f / Mathf.PI;
    Quadrant q;
    if (azimuth > -45.0f && azimuth <= 45.0f) q = Quadrant.East;
    else if (azimuth > 45.0f && azimuth <= 135.0f) q = Quadrant.North;
    else if (azimuth > -135.0f && azimuth <= -45.0f) q = Quadrant.South;
    else q = Quadrant.West;
    return (q, pos.Length());
}

(Quadrant q, _) = GetJoystickQuadrantAndMagnitude();
if (q == Quadrant.South)
{
    // Do something.
}
Kautz answered 22/4, 2022 at 2:35 Comment(0)
N
-2

What about like this?

public bool OptionalOutParamMethod([Optional] ref string pOutParam)
{
    return true;
}

You still have to pass a value to the parameter from C# but it is an optional ref param.

Nebuchadnezzar answered 24/10, 2014 at 21:47 Comment(2)
“You still have to pass a value to the parameter from C#”... This makes it not optional.Skardol
C# ignores the [Optional] annotation. This does not help.Galasyn
E
-4
void foo(ref int? n)
{
    return null;
}
Epanodos answered 15/6, 2016 at 13:47 Comment(3)
Please add some explanation on your code to make everyone understand the concept easilyDysuria
Although this code may answer the question, providing additional context regarding why and/or how it answers the question would significantly improve its long-term value. Please edit your answer to add some explanation.Philia
This will result in a syntax error as method has void return type. Also, does not answer question.Chintz

© 2022 - 2024 — McMap. All rights reserved.