TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #2289 2.62K
День 1895.
Сопоставление с Образцом и Компилятор Могут Удивлять
Сопоставление с образцом — мощная функция C#. Она позволяет сопоставлять значение с шаблоном и извлекать информацию из значения. Компилятор творит за вас волшебство, но иногда этим и поражает.

Вот такой фрагмент кода рассылки писем:
Console.WriteLine(
GetHashCode(new PostCard { Subject = "Test" }));

static int GetHashCode(Document doc)
{
return doc switch
{
PostCard { Subject: { } s, Text: { } t }
=> HashCode.Combine(s, t),
Mail { Subject: { } s }
=> s.GetHashCode(),
_ => 0,
};
}

public class Document;
public class Mail : Document
{
public string? Subject { get; set; }
}
public class PostCard : Mail
{
public string? Text { get; set; }
}

Функция GetHashCode принимает Document и возвращает хэш-код. Она использует сопоставление с образцом для сопоставления типа и получения хэш-кода. Интересная часть PostCard { Subject: { } s, Text: { } t } по сути гарантирует, что Subject и Text не null. Второе условие проверяет только базовый тип (мы идём от более конкретного к более общему). В чём тут сюрприз? В том, что код выдаёт предупреждение:
Program.cs(11,9): Warning CS8602 : Dereference of a possibly null reference (Разыменование возможно нулевой ссылки).
Вот в этом месте
Mail { Subject: { } s }
=> s.GetHashCode(),

Учитывая, что мы явно проверяем, что Subject не null, почему компилятор на это жалуется? Для этого нам нужно взглянуть на сгенерированный компилятором код:
internal static GetHashCode(Document doc)
{
PostCard postCard = doc as PostCard;
string subject2;
if (postCard != null)
{
string subject = postCard.Subject;
if (subject != null)
{
string text = postCard.Text;
if (text == null)
{
Mail mail = (Mail)doc;
subject2 = mail.Subject;
goto IL_0057;
}
return HashCode.Combine(subject, text);
}
}
else
{
Mail mail = doc as Mail;
if (mail != null)
{
subject2 = mail.Subject;
if (subject2 != null)
{
goto IL_0057;
}
}
}
return 0;
IL_0057:
return subject2.GetHashCode();
}

Кажется, статический анализатор видит проблему в операторе goto в ветке PostCard. Заметьте, что subject проверяется на null, a subject2 нет.

Так что, если в вашем коде есть именно такой шаблон, возможно, вам стоит об этом знать. Оговорюсь, что случаи, когда этот код фактически выдаст исключение NullReferenceException – крайне редкие. Вот такой вымышленный пример:
public class Mail : Document
{
private string? subject;
public string? Subject
{
get
{
Console.WriteLine("Вызвали get");
var r = subject;
subject = null;
return r;
}
set => subject= value;
}
}

В этом случае при вызове
Console.WriteLine(
GetHashCode(new PostCard { Subject = "Test" }));

аксессор get свойства Mail.Subject будет вызван дважды:
1) в строке string subject = postCard.Subject;
2) в строке subject2 = mail.Subject;
И из-за нашего специального кода get выше, во втором случае get выдаст null, поэтому return subject2.GetHashCode(); выбросит NullReferenceException. Выполнив этот код, вы увидите «Вызвали get» в консоли дважды перед исключением.

Источник: https://steven-giesel.com/blogPost/9892bd03-96bf-4ab0-aacc-973a99d5f5e0/pattern-matching-and-the-compiler-can-be-surprising
  • 👍 10
More from @netdeveloperdiary
  1. Oct 8, 2026День 2808. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Начало В ИТ есть се…
  2. Oct 7, 2026День 2807. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Окончание Нач…
  3. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
  4. Oct 6, 2026День 2806. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Начало Больши…
  5. Oct 5, 2026День 2805. #ЧтоНовенького #NET11 Аргументы в Выражениях Коллекций в C#15 В C#15 реализован…
  6. Oct 4, 2026День 2804. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →