С древних времен в языке 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. Вы также можете высказать свое мнение по ряду других предложений:
- Разрешить использование отдельных операторов потока управления в элементах с телом выражения.
- Разрешить вывод типа для членов класса с автоинициализаторами и методами (используйте ключевые слова «var»/«auto»).
- Добавьте оператор «of» для приведения типов справа, чтобы в некоторых случаях избежать анти-шаблона «(item as Type).
Member» и ада в круглых скобках.
NET #C++
-
Введение В Облачные Вычисления
19 Oct, 24 -
Должны Ли Android И Symbian Объединиться?
19 Oct, 24 -
Трансляции Steam Теперь Доступны Всем!
19 Oct, 24 -
Приложения Linux В Windows? Легко!
19 Oct, 24 -
Apache, Viewstate И Десериализация
19 Oct, 24