И Джон Скит, и Эрик Липперт подробно рассказывают о том, как ведет себя компилятор, почему он работает так, как он работает, и так далее, но я не могу точно сказать, есть ли решение для этого варианта использования во всем этом .
У меня очень похожие методы расширения в одной из моих собственных библиотек классов (и тем не менее, я редко использую их). Одна вещь, которую я сделал по-другому, состояла в том, чтобы различать , обнуляемый и обнуляемый (NullIf
), и , не обнуляемый и обнуляемый (ToNullIf
). Ваш NullIf
для типов значений - это то, что я назвал ToNullIf
.
Допустим, вы хотите начать с NullIf
, который работает в общем случае для любого типа, который можно обнулять. Вы не можете иметь оба в одном классе, потому что ограничение не является частью сигнатуры метода. Чтобы обойти это, вы можете поместить их в отдельные классы.
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;
}
}
Компилятор выберет правильный метод для ссылочных типов и типов значений, допускающих значение NULL, так, как это описывают Джон Скит и Эрик Липперт в своих соответствующих блогах.
Различие, которое я упомянул выше, включает метод расширения 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
}
}
Если вас не интересует соотношение между ссылочными типами и типами значений, допускающими значение NULL, в названии методов для случая от Nullable до Nullable , нет никаких причин, по которым вы не можете удалить расширение ExtensionMethodsForValueTypes.NullIf
Способ выше и переименуйте ToNullIf
в NullIf
. В конечном счете, именно разделение на различные классы решает исходную проблему.
Последнее замечание: Ничто из этого не учитывает обнуляемые и ненулевые ссылочные типы в C # 8.0, отчасти потому, что оно новое, а отчасти потому, что различие просто может не может быть сделано или, если это возможно, требует совершенно другой техники.