В VB.NET это происходит:
Dim x As System.Nullable(Of Decimal) = Nothing
Dim y As System.Nullable(Of Decimal) = Nothing
y = 5
If x <> y Then
Console.WriteLine("true")
Else
Console.WriteLine("false") '' <-- I got this. Why?
End If
Но в С# это происходит:
decimal? x = default(decimal?);
decimal? y = default(decimal?);
y = 5;
if (x != y)
{
Debug.WriteLine("true"); // <-- I got this -- I'm with you, C# :)
}
else
{
Debug.WriteLine("false");
}
Почему существует разница?
VB.NET и С#.NET - это разные языки, созданные разными командами, которые делали разные предположения об использовании; в этом случае семантика сравнения NULL.
Мои личные предпочтения относятся к семантике VB.NET, которая по сути дает NULL семантику "Я еще не знаю". Тогда сравнение 5 с "Я еще не знаю". естественно, "я еще не знаю"; т.е. NULL. Это имеет дополнительное преимущество для зеркалирования поведения NULL в (большинство, если не все) баз данных SQL. Это также более стандартная (чем С#) интерпретация трехзначной логики, как описано здесь.
Команда С# сделала разные предположения о том, что означает NULL, что приводит к различию в поведении, которое вы показываете. Эрик Липперт написал блог о значении NULL в С#. Per Eric Lippert: "Я также написал о семантике нулей в VB/VBScript и JScript здесь и здесь".
В любой среде, в которой возможны значения NULL, важно признать, что закон Исключенного среднего (т.е. что A или ~ A тавтологически истинно) больше нельзя полагаться.
Update:
A bool
(в отличие от bool?
) может принимать значения TRUE и FALSE. Однако языковая реализация NULL должна определять, как NULL распространяется через выражения. В VB выражения 5=null
и 5<>null
BOTH возвращают false. В С# из сопоставимых выражений 5==null
и 5!=null
только second сначала [обновлено 2014-03-02 - PG] возвращает false. Однако в ЛЮБОМ окружении, поддерживающем нуль, программисту необходимо знать таблицы истинности и нулевое распространение, используемое этим языком.
Потому что x <> y
возвращает Nothing
вместо true
. Он просто не определен, так как x
не определен. (аналогично SQL null).
Примечание: VB.NET Nothing
< > С# null
.
Вам также нужно сравнить значение a Nullable(Of Decimal)
, только если оно имеет значение.
Таким образом, VB.NET выше сравнивается с аналогичным (что выглядит менее корректно):
If x.HasValue AndAlso y.HasValue AndAlso x <> y Then
Console.WriteLine("true")
Else
Console.WriteLine("false")
End If
Спецификация языка VB.NET :
7.1.1 Типы допустимых значений... Тип с нулевым значением может содержать те же значения, что и значение, не равное нулю версию типа, а также нулевое значение. Таким образом, для нулевых тип значения, присваивание Nothing переменной типа устанавливает значение от переменной до нулевого значения, а не нулевого значения значения тип.
Например:
Dim x As Integer = Nothing
Dim y As Integer? = Nothing
Console.WriteLine(x) ' Prints zero '
Console.WriteLine(y) ' Prints nothing (because the value of y is the null value) '
Nothing
не возвращает. :-)
Посмотрите на сгенерированный CIL (я преобразовал оба в С#):
С#:
private static void Main(string[] args)
{
decimal? x = null;
decimal? y = null;
y = 5M;
decimal? CS$0$0000 = x;
decimal? CS$0$0001 = y;
if ((CS$0$0000.GetValueOrDefault() != CS$0$0001.GetValueOrDefault()) ||
(CS$0$0000.HasValue != CS$0$0001.HasValue))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
}
Visual Basic:
[STAThread]
public static void Main()
{
decimal? x = null;
decimal? y = null;
y = 5M;
bool? VB$LW$t_struct$S3 = new bool?(decimal.Compare(x.GetValueOrDefault(), y.GetValueOrDefault()) != 0);
bool? VB$LW$t_struct$S1 = (x.HasValue & y.HasValue) ? VB$LW$t_struct$S3 : null;
if (VB$LW$t_struct$S1.GetValueOrDefault())
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
}
Вы увидите, что сравнение в Visual Basic возвращает Nullable <bool> (не bool, false или true!). И undefined, преобразованный в bool, является ложным.
Nothing
по сравнению с тем, что всегда есть Nothing
, а не false в Visual Basic (это то же самое, что и в SQL).
Проблема, наблюдаемая здесь, является частным случаем более общей проблемы, которая заключается в том, что число различных определений равенства, которые могут быть полезны, по крайней мере, в некоторых обстоятельствах, превышает количество общедоступных способов их выражения. Эта проблема в некоторых случаях усугубляется неудачной верой в то, что она путается иметь разные способы проверки равенства, дают разные результаты, и такую путаницу можно избежать, если бы различные формы равенства приносили одинаковые результаты, когда это возможно.
В действительности основной причиной путаницы является ошибочное убеждение в том, что различные формы тестирования равенства и неравенства должны давать одинаковый результат, несмотря на то, что различные семантики полезны в разных обстоятельствах. Например, с арифметической точки зрения полезно иметь Decimal
, которые отличаются только числом конечных нулей, сравниваемых как равные. Аналогично для double
значений, таких как положительный нуль и отрицательный нуль. С другой стороны, с точки зрения кеширования или интернирования, такая семантика может быть смертельной. Предположим, например, что a Dictionary<Decimal, String>
такое, что myDict[someDecimal]
должно равняться someDecimal.ToString()
. Такой объект представляется разумным, если бы у него было много значений Decimal
, которые нужно было преобразовать в строку, и ожидалось, что их будет много дубликатов. К сожалению, если использовать такое кэширование для преобразования 12.3 m и 12.40 m, за которым следуют 12.30 & middot; m и 12.4 m, последние значения будут давать "12.3" и "12.40" вместо "12.30" и "12,4".
Возвращаясь к рассматриваемому вопросу, существует более чем один разумный способ сравнения объектов с нулевым значением для равенства. С# считает, что оператор ==
должен отражать поведение Equals
. VB.NET считает, что его поведение должно отражать поведение других языков, поскольку любой, кто хочет поведение Equals
, может использовать Equals
, В некотором смысле правильным решением было бы иметь трехстороннюю конструкцию "if" и требовать, чтобы, если условное выражение возвращает трехзначный результат, код должен указать, что должно произойти в случае null
. Поскольку это не вариант с языками, как они есть, следующей лучшей альтернативой является просто узнать, как работают разные языки, и признать, что они не совпадают.
Кстати, оператор Visual Basic "Is", который отсутствует в C, может быть использован для проверки того, является ли нулевой объект, по сути, нулевым. Хотя можно обоснованно задать вопрос о том, должен ли тест if
принимать Boolean?
, если нормальные операторы сравнения возвращают Boolean?
, а не Boolean
, когда вызывается для типов с нулевым значением, является полезной функцией. Кстати, в VB.NET, если попытаться использовать оператор равенства, а не Is
, вы получите предупреждение о том, что результат сравнения всегда будет Nothing
, и следует использовать Is
, если вы хотите проверьте, что что-то пустое.
== null
. А проверка того, имеет ли тип значения, допускающий значение NULL, выполняется с помощью .hasValue
. Какой Is Nothing
операторе Is Nothing
? В C # есть is
но он проверяет совместимость типов. В свете этого я действительно не уверен, что пытается сказать ваш последний абзац.
null
, хотя оба языка рассматривают это как синтаксический сахар для проверки HasValue
, по крайней мере в тех случаях, когда тип известен (я не уверен, какой код генерируется для генериков).
Может быть пост поможет вам:
Если я правильно помню, "Nothing" в VB означает "значение по умолчанию". Для типа значения это значение по умолчанию для ссылочного типа должно быть нулевым. Таким образом, не присваивая ничего структуре, это не проблема.
Это определенная странность VB.
В VB, если вы хотите сравнить два типа с нулевым значением, вы должны использовать Nullable.Equals()
.
В вашем примере это должно быть:
Dim x As System.Nullable(Of Decimal) = Nothing
Dim y As System.Nullable(Of Decimal) = Nothing
y = 5
If Not Nullable.Equals(x, y) Then
Console.WriteLine("true")
Else
Console.WriteLine("false")
End If
Nullable<>.Equals()
. Можно ожидать, что он будет работать так же (как это делает C #).
Ваш код VB просто неверен - если вы измените "x < > y" на "x = y", в результате вы все равно получите "false". Наиболее распространенным способом выражения this для экземпляров с нулевым значением является "Not x.Equals(y)", и это приведет к тому же поведению, что и "x!= Y" в С#.
x
является nothing
, в этом случае x.Equals(y)
выдаст исключение.
default(decimal?)
Возвращает 0, а неnull
.