泛型,可空,类型推断和函数签名冲突

时间:2012-12-19 14:40:44

标签: c# generics asynchronous nullable method-signature

我有这段代码:

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
}

我希望我的两种方法具有相同的名称。 这可能吗?

我的问题:

  • 我必须编写两种不同的方法,因为返回类型(如果请求失败,我希望它为null,或者如果请求成功则为值)Nullable<T>如果T是值如果T是引用类型,则为T的类型和实例。
  • async不允许ref / out,因此如果没有T类型的方法参数,则不会推断T,并且我的两个方法不能具有相同的名称(签名冲突) ,因为如果没有推断出T,通用约束不适用于签名冲突解决方案

目前这段代码有效,但我不喜欢“RequestValue1”和“RequestValue2”之间的这种奇怪的函数调用。

2 个答案:

答案 0 :(得分:1)

您可以删除约束,并让调用者将Nullable<T>类型传递给您的方法(即调用RequestValue<int?>(cmd)而不是RequestValue<int>(cmd))。您可以在运行时确保可空性,如下所示:

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
}

答案 1 :(得分:0)

可以有方法签名,如:

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

没有冲突。尝试传递不可为空的结构类型将失败,但是在给定可为空类型或类类型的参数的情况下,编译器可以选择重载没有问题。

您的方案略有不同,因为您没有传递特定类型的参数;你只是试图传递这种类型。如果没有类型的实际参数,编译器将无法确定Nullable<T>更适合一种方法而不是另一种方法。我倾向于提供一种方法,该方法使用除结果无效之外的其他方法来指示成功或失败。正常的Try模式如下所示:

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

我并不特别喜欢这种模式,因为Result不可能协变或参与类型推断。另一种选择是:

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

这种形式对协方差没有问题。另一种形式可能是:

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

其中ResultStatus是一个带有Succeeded方法的类型,如果成功则返回True,但可以让其他成员解释出现故障时的错误。如果它是一个不可变的抽象类型,它定义了一个单例Success实例,以便在工作时使用,它可以在将来扩展,以便在事情不起作用时提供任意级别的细节而不会造成任何GC压力。

不幸的是,即使out参数类型不依赖于T的表单也不能在某些上下文中使用。为了实现这一点,我们可以定义一个结构类型CommandResult<T>,它将T与成功指标结合起来,其概念上类似于Nullable<T>,但没有令人烦恼的struct约束。它的参数类型。成功指标可以是bool,也可以是如上所述的状态指示器。