Jon Skeet 和 Eric Lippert 都非常详细地介绍了编译器的行为方式、为什么它会以这种方式工作等等,但我无法确定是否有针对此用例的解决方案。
我在自己的一个类库中有非常相似的扩展方法(然而,我很少使用它们)。我做的一件不同的事情是区分可空到可空(NullIf
)与不可空到可空(ToNullIf
)。你NullIf
的 for 值类型就是我所说ToNullIf
的。
假设您想从NullIf
一般适用于任何可为空的类型的 a 开始。您不能将两者都放在同一个类中,因为约束不是方法签名的一部分。为了解决这个问题,您可以将它们放在单独的类中。
public static partial class ExtensionMethodsForValueTypes
{
// Nullable to nullable
public static T? NullIf<T>(this T? value, T? other)
where T : struct
{
return value == null || EqualityComparer<T>.Default.Equals((T)value, other) ? null : value;
}
}
public static partial class ExtensionMethodsForReferenceTypes
{
// Nullable to nullable
public static T NullIf<T>(this T value, T other)
where T : class
{
return EqualityComparer<T>.Default.Equals(value, other) ? null : value;
}
}
编译器将按照 Jon Skeet 和 Eric Lippert 在各自博客中描述的方式为引用类型和可空值类型选择正确的方法。
我上面提到的区别包括一个ToNullIf
扩展方法,它采用(不可为空的)值类型。它可以与NullIf
采用可为空值类型的类在同一个类中。但是,它也不能称为NullIf
. 出于这个原因,我将再次推迟到大师赛。
不过幸运的是,通过不同的方法名称指示提升为 nullable 实际上对于更清楚地传达意图以及在 IDE 中向您传达启示非常有用,例如当 IntelliSense 不显示NullIf
纯值类型或ToNullIf
对于可空值类型。然而,由于 IntelliSense 在 VS 2017 中的部分匹配,键入“NullIf”将显示ToNullIf
这是否可用。
partial class ExtensionMethodsForValueTypes
{
// Non-nullable to nullable
public static T? ToNullIf<T>(this T value, T other)
where T : struct
{
return EqualityComparer<T>.Default.Equals(value, other) ? (T?)null : value;
}
}
如果您想在NullIf
采用引用类型的顶部添加字符串特化,您可以,但您不能有一个没有至少一个非默认参数的默认参数,以将其与调用站点的通用版本区分开来。在您的情况下,您需要提供两个重载。没有参数的重载会ignoreCase
阻止NullIf<string>
选择,因为前者是更具体的类型匹配。带有参数的一个为您提供所需ignoreCase
的不区分大小写。
partial class ExtensionMethodsForReferenceTypes
{
public static string NullIf(this string value, string other) => NullIf(value, other, false);
public static string NullIf(this string value, string other, bool ignoreCase)
{
return String.Compare(value, equalsThis, ignoreCase) == 0 ? null : value
}
}
如果您对可空到可空情况的方法名称中引用类型和可空值类型之间的奇偶校验不感兴趣,那么您没有理由不能放弃ExtensionMethodsForValueTypes.NullIf
上面的扩展方法并重命名ToNullIf
为NullIf
. 最终,解决原始问题的是分成不同的类。
最后一点:这些都没有考虑到 C# 8.0 中的可空和不可空引用类型,部分是因为它是新的,部分是因为根本无法进行区分,或者,如果可以,它需要不同的技术完全。