2

I have a generic class to represent either a result or an error, but not both:

public class Result<TValue, TError>
    where TValue : notnull
    where TError : notnull
{
    private readonly bool succeeded;
    private readonly TValue? value;
    private readonly TError? error;

    public static Result<TValue, TError> Success(TValue value) =>
        new(true, value ?? throw new ArgumentNullException(nameof(value)), default);

    public static Result<TValue, TError> Failure(TError error) =>
        new(false, default, error ?? throw new ArgumentNullException(nameof(error)));

    protected Result(bool succeeded, TValue? value, TError? error)
    {
        this.succeeded = succeeded;
        this.value = value;
        this.error = error;
    }
    
    public bool Successful(
        [NotNullWhen(true)] out TValue? value,
        [NotNullWhen(false)] out TError? error)
    {
        if (succeeded)
        {
            value = this.value!;
            error = default;
            return true;
        }
        else
        {
            value = default;
            error = this.error!;
            return false;
        }
    }
}

This works as I want for classes. For example, if TValue is object then the value field and value parameters are object?. However, I've realised that when a generic type parameter is a value type, decorating it with the ? doesn't make it Nullable. This is presumably because doing so would change the underlying type rather than just commenting on its nullability. Is there any way to reference the Nullable version of a generic type?

Giles
  • 1,331
  • 1
  • 15
  • 30
  • 1
    I suppose you know that if you constrain `where TValue : struct` then `TValue?` will always mean `Nullable`. But then you cannot use your type for situations where `TValue` is a reference type. – Jeppe Stig Nielsen Jun 08 '22 at 10:34
  • Thanks @JeppeStigNielsen. Yes, I had a go at creating the , , and combinations, but it'd be a shame because as well as the replication, each version would need a different name. – Giles Jun 08 '22 at 10:38
  • 1
    Maybe you must give up all the `null` stuff and write or find an [option type](https://en.wikipedia.org/wiki/Option_type) to use instead (like `Maybe` or `Optional` or something; where `T` is not constrained to value types), like in thread [Optional return in C#.Net](https://stackoverflow.com/questions/16199227/) or others. Of course, syntax like `??` and attributes like `[NotNullWhen(...)]` will not work with that. – Jeppe Stig Nielsen Jun 08 '22 at 10:56
  • Thanks @JeppeStigNielsen. It might be the way to go. I did like the [NotNullWhen], as the code analysis works well from the caller site, but that doesn't seem to work with Nullables anyway. Thanks for your advice. – Giles Jun 08 '22 at 11:25

1 Answers1

2

This is language limitation. See official docs

If the type argument for T is a value type, T? references the same value type, T. For example, if T is an int, the T? is also an int.

picolino
  • 4,856
  • 1
  • 18
  • 31