3

Using C# 8+ with nullable context enabled, I have a method which returns

  1. An enum representing various error codes, or success;
  2. An object (if success) or null (if error)

as a ValueTuple. Aside from the null-forgiving operator, is there a way I can tell the compiler that the object is not null if the enum value indicates success?

private (EnumResult, SomeClass?) DoThing(...)
{
    if (...)
        return (EnumResult.Error1, null);

    if (...)
        return (EnumResult.Error2, null);

    return (EnumResult.Success, new SomeClass(...));
}
(EnumResult result, SomeClass? someClass) = DoThing(...);

if (result == EnumResult.Success)
{
    // `someClass` should not be null here, but the compiler doesn't know that.
}

I am aware there are nullable static analysis attributes which can be applied when using a bool return and an out parameter:

private bool TryDoThing(..., [NotNullWhen(true)] out SomeClass? someClass)
{
    if (...)
    {
        someClass = null;
        return false;
    }

    someClass = new SomeClass(...);
    return true;
}
if (TryDoThing(..., out SomeClass someClass))
{
    // The compiler knows that `someClass` is not null here.
}

But I couldn't determine how to apply something similar when returning a ValueTuple.

I am using an enum rather than a bool or throwing exceptions because the result code is communicated across a named pipe, where the process on the other side parses it back into an enum and then reacts according to the specific error. I am using a ValueTuple rather than an out parameter out of personal preference.

Daniel Smith
  • 438
  • 4
  • 7
  • 1
    I don't know if implicit conversion will work, but the Boolean value of false = 0, and basically on implicit conversion, anything other than 0 is then true. So you could make your enum success value = 0, and then use the `[MaybeNullWhen(true)]` (if I'm not getting that mixed up, and again if it will do implicit conversion). – B.O.B. Apr 27 '21 at 21:48
  • 1
    Stop trying to reinvent exception handling. If something goes wrong, throw an exception. The calling code can unpack the exception and decide what to do with it. Trying to pass exception information in a return value is pointless since there is already a mechanism there to get it from callee to caller. – Dave Holden Apr 27 '21 at 22:31
  • 1
    Would it make sense to maybe create a static `SomeClass.Empty` value? That way you can avoid the nullable type complications, and just return that `Empty` value when reaching some condition that would otherwise return null in your ValueTuple. Not sure if your pipe is expecting a null value to operate on though, that could get a bit messy to refactor. – Aelarion Apr 28 '21 at 02:34
  • 1
    @DaveHolden, Exceptions are very expensive and there is good reason to "reinvent" them. MS and others already did this in many places like returning error codes, or using "Try" pattern. – greenoldman Apr 28 '21 at 06:21
  • @B.O.B. Implicit conversion and casting both give a compiler error. `Convert.ToBoolean()` isn't accepted either, as attribute arguments must be constant values. Even if it did work, it would require using an `out` parameter rather than multiple return values, which was the main thing I was wondering about. Thanks though—your comment helped me better understand the limitations of the existing attributes. The answer to my question is _probably_ "no." – Daniel Smith Apr 28 '21 at 12:47
  • @DaveHolden To follow on what greenoldman said, they are also considered glorified GoTo statements. Here's a topic on Exception handling being bad (https://stackoverflow.com/questions/1736146/why-is-exception-handling-bad) which has some useful info and a useful article I read awhile ago (http://www.lighterra.com/papers/exceptionsharmful/) ... – B.O.B. Apr 28 '21 at 15:31
  • ... also here is another topic on Exceptions being expensive with some useful links to look into https://stackoverflow.com/questions/891217/how-expensive-are-exceptions-in-c ... – B.O.B. Apr 28 '21 at 15:32

1 Answers1

1

No. At this time, the MaybeNullWhen/NotNullWhen attributes only work to signal that the null state of an out parameter depends on a bool return value.

There is currently no plan to allow the null state of a variable to depend on the value of an enum return value, for example.

There is also no plan to allow an interdependence between the elements of a tuple return value, i.e. the (object? result, bool ok) Method() pattern. If you are interested in such functionality getting added to the language, feel free to start a discussion on how it would work at https://github.com/dotnet/csharplang/discussions.

Rikki Gibson
  • 4,136
  • 23
  • 34