Question

I've this code :

public async static Task<T?> RequestValue1<T>(Command requestCommand)
                    where T : struct 
{
    // Whatever
}

public async static Task<T> RequestValue2<T>(Command requestCommand)
                    where T : class 
{
    // Whatever
}

I want to have the same name for my two methods. Is this even possible ?

My problems:

  • I have to write two different methods because of the return type (I want it to be null if the request failed or a value if the request succeed) which is Nullable<T> if T is a value type, and an instance of T if T is a reference type.
  • async doesn't allow ref/out, so without a method argument of type T, T isn't inferred and my two methods cannot have the same name (signature conflict, as generic constraints doesn't works for signature conflict resolution if T isn't inferred)

Currently this code works but I don't like this strange function calls between "RequestValue1" and "RequestValue2".

Was it helpful?

Solution

You could create your own Option type and use that to indicate if a value is returned or not:

public async static Task<Option<T>> RequestValue<T>(Command requestCommand) {
  ...
}

Update: the intent of the Option<T> type is to replace nulls and Nullable<T>s, but if you'd still like to use them, you can use these extension methods to bridge the gap:

public static class OptionExtensions {
  public static T? GetNullableValue<T>(this Option<T> option) where T : struct {
    return option.HasValue ? (T?)option.Value : null;
  }
  public static T GetValueOrNull<T>(this Option<T> option) where T : class {
    return option.HasValue ? option.Value : null;
  }
}

OTHER TIPS

You can drop the constraint, and make the callers pass a Nullable<T> type to your method (i.e. call RequestValue<int?>(cmd) instead of RequestValue<int>(cmd)). You can ensure nullability at runtime, like this:

public async static Task<T> RequestValue<T>(object arg) {
    var t = typeof (T);
    if (!t.IsClass && (!t.IsGenericType || t.GetGenericTypeDefinition() != typeof(Nullable<>))) {
        throw new ArgumentException("T");
    }
    // Whatever
}

One can have method signatures like:

    static void test1<T>(Nullable<T> param) where T:struct;
    static void test1<T>(T param) where T:class;

without conflict. Attempting to pass a non-nullable struct type will fail, but the compiler will have no problem selecting an overload given a parameter of either a nullable type or a class type.

Your scenario is a little different, since you aren't passing a parameter of a particular type; you're just trying to pass the type itself. Without an actual parameter of the type, the compiler won't be able to decide that a Nullable<T> is a better fit for one method than the other. My inclination would be to offer a method which uses some other means than the nullity of the result to indicate success or failure. The normal Try pattern would look like:

static bool RequestValue1<T>(Command requestCommand, out Task<T> Result);

I don't especially like that pattern, since it's impossible for Result to be covariant or participate in type inference. An alternative would be:

static Task<T> RequestValue1<T>(Command requestCommand, out bool Success);

Such a form would have no problem with covariance. An alternative form would be something like:

static Task<T> RequestValue1<T>(Command requestCommand, out ResultStatus Status);

where ResultStatus would be a type with a Succeeded method that returns True in case of success, but could have other members that explain what's wrong in case of failure. If it's an immutable abstract type which defines a singleton Success instance for use when things work, it could be extended in future to provide an arbitrary level of detail when things don't work without causing any GC pressure when they do.

Unfortunately, even the forms where the out parameter type doesn't depend upon T can't be used in some contexts. To allow for that, one could define a struct type CommandResult<T> which combines a T with a success indicator, in a fashion conceptually similar to Nullable<T> but without annoying struct constraint on its parameter type. The success indicator could either be a bool, or could be a status indicator as described above.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top