gpt4 book ai didi

c# - 为什么泛型约束不能帮助编译器在具有可选参数的多态方法中做出决定?

转载 作者:太空宇宙 更新时间:2023-11-03 12:04:31 27 4
gpt4 key购买 nike

给定 2 个静态重载方法,一个是通用的,一个不是:

public static T? NullIf<T>(this T value, T equalsThis) 
where T : struct // value types including enums
{
return EqualityComparer<T>.Default.Equals(value, equalsThis)
? (T?)null
: value;
}

public static string NullIf(this string value, string equalsThis, bool ignoreCase = false)
{
return String.Compare(value, equalsThis, ignoreCase) == 0 ? null : value;
}

测试代码:

string s = "None";
string result = s.NullIf("None");

生成编译错误,因为它更喜欢泛型:

Error CS0453: The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'ExtensionMethods.NullIf(T, T)'

如果可选的 ignoreCase 参数由调用者提供或从方法中移除,或者如果受限的泛型方法被移除,它会编译。

为什么编译器不使用 where 约束来消除对泛型的考虑,因为它认识到不兼容?

最佳答案

Jon Skeet 和 Eric Lippert 都非常详细地介绍了编译器的行为方式、其工作方式的原因等等,但我无法确定是否有针对所有这些用例的解决方案.

我自己的一个类库中有非常相似的扩展方法(但我很少使用它们)。我所做的一件事是区分可空到可空 ( NullIf ) 与 不可空到可空 ( ToNullIf )。值类型的 NullIf 就是我所说的 ToNullIf

假设您想要从 NullIf 开始,它通常适用于任何可为 null 的类型。你不能在同一个类中同时拥有这两个,因为约束不是方法签名的一部分。为了解决这个问题,您可以将它们放在不同的类中。

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 在各自博客中描述的方式为引用类型和可为 null 的值类型选择正确的方法。

我上面提到的区别包括一个 ToNullIf 扩展方法,它采用(不可空)值类型。它可以与采用可空 值类型的 NullIf 在同一类中。但是,它不能也称为 NullIf 。出于这个原因,我会再次服从大师赛。

不过,幸运的是,通过不同的方法名称指示对可空的提升实际上可以非常有助于更清楚地传达意图,以及在 IDE 中向您传达启示,例如当 IntelliSense 不显示 NullIf 时纯值类型或 ToNullIf 可空值类型。然而,由于 VS 2017 中的部分匹配 IntelliSense,如果可用,键入“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
}
}

如果您对nullable to nullable 情况下方法名称中引用类型和可为空值类型之间的奇偶校验不感兴趣,则没有理由不删除 ExtensionMethodsForValueTypes.NullIf 扩展方法上面并将 ToNullIf 重命名为 NullIf 。最终,解决原始问题的是分成不同的类。

最后一点:这些都没有考虑 C# 8.0 中的可空与不可空引用类型,部分原因是它是新的,部分原因是区别根本无法制造,或者即使可以,也需要完全不同的技术。

关于c# - 为什么泛型约束不能帮助编译器在具有可选参数的多态方法中做出决定?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55736977/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com