Должны Ли Мы Исправить Неожиданное Поведение В C# 7 Или Оставить Все Как Есть, Усложняя Синтаксис Языка, Чтобы Компенсировать Это?

С древних времен в языке C# существовал оператор is, назначение которого вполне понятно.

  
  
  
  
  
  
  
  
  
   

if (p is Point) Console.WriteLine("p is Point"); else Console.WriteLine("p is not Point or null");

Кроме того, его можно использовать для проверки нуля

if (p is object) Console.WriteLine("p is not null"); if (p is null) Console.WriteLine("p is null");

Новая функция сопоставления с образцом, анонсированная в C# 7

if (GetPoint() is Point p) Console.WriteLine($"X={p.X} Y={p.Y}"); else Console.WriteLine("It is not Point."); if (GetPoint() is var p) Console.WriteLine($"X={p.X} Y={p.Y}"); else Console.WriteLine("It is not Point.");

Вопрос в том, что произойдет в обоих случаях, если метод вернет значение null? Вы уверены? Вы, вероятно, уже сталкивались с этой странной особенностью языка раньше, поэтому она не станет для вас сюрпризом, но недавно я был весьма удивлен (спасибо JetBrains за подсказку!), что 'GetPoint() is var p' всегда правда, но 'GetPoint() - это AnyType p' нет. Я всегда думал о «var» как о своего рода белом ящике, который позволяет вам не указывать явно тип переменной, если он может быть выведен компилятором [вывод типа].

В C# 7, по-моему, прокралась подмена значения оператора 'var', и теперь это может означать что-то другое.

Я, конечно, поинтересовался, почему было принято такое решение, и спросил ребят на официальный репозиторий на гихабе , где предлагают и обсуждают нововведения в языке, но не получили внятного, аргументированного ответа с примерами кода, почему нужно было сделать так, а не иначе.

Ответы ограничились лишь тем, что данное решение было принято в результате длительных дискуссий, однако по предоставленным ссылкам мне не удалось найти значимых аргументов в защиту принятого решения; это было просто постулировано.

Но можно ли было сделать это лучше? Взглянем.



public static class LanguageExtensions { public static bool IsNull(this object o) => o is null; public static bool Is<T>(this object o) => o is T; public static bool Is<T>(this T o) => o != null; /* or same 'o is T' */ public static bool Is<T>(this T o, out T x) => (x = o) != null; /* or same '(x = o) is T' */ /* .

*/ public static T As<T>(this object o) where T : class => o as T; public static T Of<T>(this object o) => (T) o; } public Point GetPoint() => null; // new Point { X = 123, Y = 321 }; if (GetPoint().

Is(out AnyType p) Console.WriteLine($"X={p.X} Y={p.Y}"); else Console.WriteLine("It is not Point."); if (GetPoint().

Is(out var p) Console.WriteLine("o is Any Type"); else Console.WriteLine("It is not Point.");

На мой взгляд, все достаточно очевидно и удобно.

Но самое страшное, на мой взгляд, то, что для компенсации недостатков принятого решения предлагается ввести новый синтаксис!

if (GetPoint() is AnyType p) Console.WriteLine($"X={p.X} Y={p.Y}"); else Console.WriteLine("It is not Point."); if (GetPoint() is {} p) Console.WriteLine("o is Any Type"); else Console.WriteLine("It is not Point."); if (GetPoint() is var p) Console.WriteLine("Always true");

Более того, это влияет на синтаксис еще одной, еще не объявленной, функции рекурсивного сопоставления с образцом.

Возможно

if (GetPoint() is AnyType p { X is int x, Y is int y}) Console.WriteLine($"X={x} Y={y}"); else Console.WriteLine("It is not Point."); if (GetPoint() is var p { X is int x, Y is int y}) Console.WriteLine($"X={x} Y={y}"); else Console.WriteLine("It is not Point."); if (GetPoint() is { X is int x, Y is int y}) Console.WriteLine($"X={x} Y={y}"); else Console.WriteLine("It is not Point.");

Но предполагается (насколько я понимаю)

if (GetPoint() is AnyType { X is int x, Y is int y} p) Console.WriteLine($"X={p.X} Y={p.Y}"); else Console.WriteLine("It is not Point."); if (GetPoint() is var { X is int x, Y is int y} p) Console.WriteLine($"X={p.X} Y={p.Y}"); else Console.WriteLine("It is not Point."); // but if (GetPoint() is var p) Console.WriteLine($"Always true"); if (GetPoint() is { X is int x, Y is int y} p) Console.WriteLine($"X={p.X} Y={p.Y}"); else Console.WriteLine("It is not Point.");

С моей точки зрения, все выглядит наперекосяк, грядет очередное «расширение» концепции блока кода '{ }'.

Но теперь мы подходим к основной проблеме — истинное выражение «x is var y» всегда уже есть в релизе, поэтому изменение его поведения — это критическое изменение, которое, по словам ребят из репозитория, сделать сейчас практически невозможно.

Я прекрасно понимаю их опасения, но как разработчик, стремящийся к чистоте кода, я готов смириться даже с таким кардинальным изменением ради чистоты и ясности синтаксиса языка.

Более того, это исправление можно сделать максимально аккуратно в контексте предстоящих функций C# 8. Типы нулевых ссылок .

Например, у нас есть метод

public bool SureThatAlwaysTrue(AnyType item) => item is var x;

Если скомпилировать его на C# 8, но с условием, что выражение может быть 'false', если 'item == null', то поведение метода не изменится, так как в контексте C# 8 выражение 'AnyType item ' предполагает, что 'item != null' (компилятор не игнорирует выражение 'SureThatAlwaysTrue(null)' и отображает предупреждающее сообщение в случае 'SureThatAlwaysTrue(null)').

Сообщение можно удалить намеренно только с помощью знака '!'.

оператор.

следующим образом: «SureThatAlwaysTrue(null!)» или перепишите метод следующим образом:

public bool SureThatAlwaysTrue(AnyType? item) => item is var x;

Проблема нарушения изменений остается только для типов значений, допускающих значение NULL, которые уже присутствуют в C# 7.

public bool SureThatAlwaysTrue(int? item) => item is var x;

Такой метод, даже если появится предупреждающее сообщение, необходимо будет реорганизовать вручную [критическое изменение].

Все ключевые моменты я рассказал максимально честно, так как сам их понимаю и вижу, поэтому теперь мне очень интересно ваше мнение как разработчиков: предпочитаете ли вы оставить все как есть и в будущем мириться со сложным синтаксисом , или вы готовы пойти на не столь масштабное ломающее изменение ради сохранения? чистота и ясность языка? Тщательно подумайте, прежде чем принять решение, поскольку здесь есть довольно убедительные плюсы и минусы.

Более детальное изучение вопроса и связанные с ним дискуссии не помешали бы.

Для информации: Вопрос: что означает «вар»? Вы можете проголосовать за или против по ссылке ниже с более подробными предложениями по улучшению синтаксиса языка: Переосмысление сопоставления с образцом (в контексте C# 8 Nullable Reference Types) P.S. Вы также можете высказать свое мнение по ряду других предложений:

Теги: #C++ #.

NET #C++

Вместе с данным постом часто просматривают: