Вопрос или проблема
Операторы объединения с нулем с операндами разных типов
Я понимаю, что вы не можете объединить разные типы в один результат, но не понимаю, почему это так.
Например:
Guid? ID;
int ID2;
var ID3 = ID ?? ID2;
Этот код не работает, независимо от того, является ли ID
нулевым или каков тип ID3
: var, object и т.д.
Может кто-то объяснить, почему это не разрешено?
Мой реальный код выглядит так:
public StandAloneMachine Get(int id, Guid? FKGuid = null, string FKName = "")
{
var parameters = new Parameters()
{
new Parameter("id", FKGuid ?? id)
};
}
У кого-нибудь есть причина, почему это не разрешено?
Таково устройство языка. Что он должен делать? Как преобразовать int
, который является 32-битным числом, в GUID
, который является структурой, состоящей из нескольких частей числа, или наоборот?
Этот код:
var ID3 = ID ?? ID2
в преобразованных типах означает (псевдокод):
(выясненный тип) = Guid? ?? int;
и оператор ??
естественно требует, чтобы типы его аргументов были совместимыми. Фактические правила достаточно сложны и могут быть найдены в спецификации языка C# здесь.
В любом случае, оператор объединения нулей возвращает одно значение, и оба его аргумента слева и справа должны быть оценены в значение одного типа. Следовательно, результат может быть либо Guid
, либо int
. (выясненный тип)
будет тогда этим типом. Тем не менее, ??
не может преобразовать значения операндов Guid
и int
в значение такого единого типа.
Примечание: Можно утверждать, что компилятор может выбрать object
как ближайший совместимый тип как с Guid
, так и с int
, и просто упаковать эти значения в объект. Однако это подорвет сам смысл строгой проверки типов. Это будет просто ‘слишком расслаблено’.
В вашем конкретном примере использование ??
кажется искусственным; старая добрая ?:
и немного большей выразительности в вашем коде были бы лучше и, возможно, даже помогли бы читаемости:
var parameters = new Parameters()
{
new Parameter("id", FKGuid.HasValue ? ToTypeParameterExpects(FKGuid.Value) : ToTypeParameterExpects(id))
};
Очевидно, что ToTypeParameterExpects
– это преобразование в любой тип, который ожидает конструктор Parameter
для значения.
Ответ или решение
Использование оператора объединения с нулевым значением (null-coalescing operator) в C# требует, чтобы операнды имели совместимые типы. Ваша проблема связана с тем, что вы пытаетесь объединить значения разных типов: Guid?
и int
.
Давайте разберем это подробнее.
Причина ограничений
Оператор ??
предназначен для возврата первого не-null значения из двух возможных значений. Это означает, что компилятор должен определить единый тип для результата, который может быть возвращен. В вашем примере:
Guid? ID;
int ID2;
var ID3 = ID ?? ID2;
Компилятор пытается вывести тип для ID3
. Первый операнд (ID
) является Guid?
, а второй (ID2
) — int
. Эти два типа несовместимы. Guid
— это структура, состоящая из нескольких частей, а int
— это 32-битное целое число.
Совместимость типов
В C# важна строгая типизация, которая требует, чтобы типы в выражениях были совместимыми. Оператор ??
не может автоматически преобразовать один тип в другой (например, Guid
в int
или наоборот), поскольку это может привести к несоответствиям в значениях, которые они представляют. Компилятор не может «угадывать», какой тип использовать, и не может просто взять object
как общий тип, так как это ослабит строгую типизацию.
Решение проблемы
В вашем конкретном случае, чтобы обойти эту проблему, воспользуйтесь тернарным оператором ?:
, чтобы явно указать, какой тип вы хотите использовать в зависимости от наличия значения:
public StandAloneMachine Get(int id, Guid? FKGuid = null, string FKName = "")
{
var parameters = new Parameters()
{
new Parameter("id", FKGuid.HasValue ? ToTypeParameterExpects(FKGuid.Value) : ToTypeParameterExpects(id))
};
}
Здесь ToTypeParameterExpects
— это функция (или метод), которая преобразует значение нужного типа в ожидаемый тип параметра. Таким образом, вы явно указываете к какому типу вы хотите привести значения в зависимости от их наличия, что делает ваш код более читаемым и понятным, а также соблюдает правила типизации C#.
Заключение
Хотя использование оператора ??
в данном случае было бы удобно, для работы с несовместимыми типами, такими как Guid?
и int
, лучше использовать тернарный оператор и ручные преобразования типов. Это гарантирует, что ваш код будет компилироваться и гарантирует соблюдение строгой типизации, которая является одним из основных принципов C#.