В данной статье мы рассмотрим новую версию языка C# 10, которая включает в себя небольшой список изменений относительно C# 9. Ниже приведены их описания вместе с поясняющими фрагментами кода. Давайте их рассмотрим.

Изменения структур

Инициализация полей структуры

Теперь в структурах можно задать инициализацию полей и свойств:

public struct User
{
    public User(string name, int age)
    {
        Name = name;
        Age = age;
    }
    string Name { get; set; } = string.Empty;
    int Age { get; set; } = 18;
}

Объявление конструктора структуры без параметров

В C# 10 станет доступно создание конструктора без параметров для структур:

public struct User
{
    public User()
    {

    }

    public User(string name, int age)
    {
        Name = name;
        Age = age;
    }

    string Name { get; set; } = string.Empty;
    int Age { get; set; } = 18;
}

Важно. Использовать конструктор без параметров можно только тогда, когда все поля и/или свойства имеют инициализаторы. Например, если не задать инициализатор Age, возникнет ошибка:

Error CS0843: Auto-implemented property 'User.Age' must be fully assigned before control is returned to the caller.

Применение выражения with к структуре

Новая версия C# делает доступным использование выражения with со структурами, как ранее это можно было сделать с записями. Пример:

public struct User
{
    public User()
    {

    }

    public User(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public string Name { get; set; } = string.Empty;
    public int Age { get; set; } = 18;
}

User myUser = new("Chris", 21);
User otherUser = myUser with { Name = "David" };

Очевидно, что изменяемое свойство (в данном случае поле Name) должно быть с публичным модификатором доступа.

Глобальное использование using

Данное нововведение позволит использовать директиву using с охватом всего проекта. Для этого необходимо перед фразой using добавить ключевое слово global:

global using "Пространство имен"

Таким образом, можно не дублировать в разных файлах одни и те же пространства имён, используя директиву using.

Важно. Применять данную конструкцию следует ПЕРЕД включениями using, которые объявлены без использования global. Пример:

global using System.Text;
using System;
using System.Linq;
using System.Threading.Tasks;
// Корректная запись

В ином случае:

using System;
using System.Linq;
using System.Threading.Tasks;
global using System.Text;
// Error CS8915
// A global using directive must precede
// all non-global using directives.

Если произошло так, что вы повторно включили одно пространство имён при условии, что оно было ранее включено с использованием ключевого слова global, то об этом будет сообщено средой разработки (IDE: 0005: Using directive is unnecessary).

namespace для всего файла

Бывает такое, что пространство имён необходимо использовать во всём файле, но это может немного сдвинуть отступы текста программы. Теперь для решения этой проблемы добавлена возможность использования namespace для всего файла. Для этого необходимо написать namespace без использования блочных фигурных скобок:

using System;
using System.Linq;
using System.Threading.Tasks;

namespace TestC10;

public class TestClass
{
    ....
}

Ранее необходимо было тянуть на весь файл открытый блок namespace:

using System;
using System.Linq;
using System.Threading.Tasks;

namespace TestC10
{
    public class TestClass
    {
        ....
    }
}

Очевидно, указать в файле можно лишь один такого рода namespace. То есть данная запись некорректна:

namespace TestC10;
namespace MyDir;
// Error CS8954
// Source file can only contain
// one file-scoped namespace declaration

как и запись такого вида:

namespace TestC10;
namespace MyDir
{
    ....
}
// Error CS8955
// Source file can not contain both
// file-scoped and normal namespace declarations.

Изменение записей

Ключевое слово class

В версии языка C# 10.0 было добавлено необязательное ключевое слово class у записей, которое лишь помогает при чтении кода определить принадлежность записи к ссылочному типу.

То есть две следующие записи идентичны:

public record class Test(string Name, string Surname);
public record Test(string Name, string Surname);

Структуры записей

Также теперь можно создавать структуры записей:

record struct Test(string Name, string Surname)

По умолчанию, свойства данной записи можно изменять, в отличие от стандартной record, в которой модификатор изменения полей – init:

string Name { get; set; }
string Surname { get; set; }

К данной структуре можно применить свойство readonly, тогда доступ к полям будет подобен обычной записи:

readonly record struct Test(string Name, string Surname);

где свойства записаны как:

string Name { get; init; }
string Surname { get; init; }

Равенство двух объектов структуры записей схоже с равенством двух обычных структур – равенство истинно, если эти два объекта хранят одни и те же данные:

var firstRecord = new Person("Nick", "Smith");
var secondRecord = new Person("Robert", "Smith");
var thirdRecord = new Person("Nick", "Smith");

Console.WriteLine(firstRecord == secondRecord);
// False
Console.WriteLine(firstRecord == thirdRecord);
// True

Стоит отметить, что для структур записей не генерируется конструктор копирования. Если определить его и использовать ключевое слово with при инициализации нового объекта, то вызываться будет оператор присваивания, вместо конструктора копирования (как это происходит при работе с record class).

Запечатывание метода ToString

Как писал мой коллега в статье о нововведениях в C# 9, у записей имеется метод ToString, который можно переопределить. Но при наследовании есть одна особенность: переопределенный вами метод ToString не будет унаследован потомками от родительской записи. Для того чтобы потомки записей наследовали метод ToString, стало доступно использование ключевого слова sealed, которое запрещает компилятору генерировать имплементацию метода ToString у дочерних записей. Данное ключевое слово указывается при переопределении метода ToString:

public sealed override string ToString()
{
    ....
}

Создадим следующую запись, включая переопределение метода ToString:

public record TestRec(string name, string surname)
{
    public override string ToString()
    {
        return $"{name} {surname}";
    }
}

И наследуем от него вторую запись:

public record InheritedRecord : TestRec
{
    public InheritedRecord(string name, string surname)
    :base(name, surname)
    {

    }
}

Теперь создадим по экземпляру каждой из записей и напечатаем результат в консоль:

TestRec myObj = new("Alex", "Johnson");
Console.WriteLine(myObj.ToString());
// Alex Johnson

InheritedRecord mySecObj = new("Thomas", "Brown");
Console.WriteLine(mySecObj.ToString());
// inheritedRecord { name = Thomas, surname = Brown}

Как видно из результата выше, метод ToString не был наследован записью InheritedRecord.

Немного изменим запись TestRec, добавив ключевое слово sealed:

public record TestRec(string name, string surname)
{
    public sealed override string ToString()
    {
        return $"{name} {surname}";
    }
}

Заново создадим два экземпляра записей и напечатаем результат в консоль:

TestRec myObj = new("Alex", "Johnson");
Console.WriteLine(myObj.ToString());
// Alex Johnson

InheritedRecord mySecObj = new("Thomas", "Brown");
Console.WriteLine(mySecObj.ToString());
// Thomas Brown

И.. ура! Метод ToString был наследован в записи InheritedRecord.

Упрощение доступа к вложенным полям и свойствам шаблонных свойств

Версия языка C# 8.0 предоставила возможность использования шаблонных свойств, с помощью которых можно удобно сопоставлять поля и/или свойства какого-либо объекта с необходимыми значениями.

Ранее, если необходимо было проверить какое-либо вложенное свойство, то приходилось громоздить конструкцию:

....{property: {subProperty: pattern}}....

Сейчас достаточно лишь привычного добавления точек между свойствами:

....{property.subProperty: pattern}....

Рассмотрим изменение на примере метода взятия 4 первых символов имени.

public record TestRec(string name, string surname);

string TakeFourSymbols(TestRec obj) => obj switch
{
    // старый способ:
    //TestRec { name: {Length: > 4} } rec => rec.name.Substring(0,4),

    // новый способ:
    TestRec { name.Length: > 4 } rec => rec.name.Substring(0,4),
    TestRec rec => rec.name,
};

Как видно из примера выше, новый тип обращения к свойствам проще и понятнее, чем был раньше.

Интерполирование константных строк

Ранее данная возможность не поддерживалась, но в новой версии языка C# можно будет интерполировать и константные строки:

const string constStrFirst = "FirstStr";
const string summaryConstStr = $"SecondStr {constStrFirst}";

Интересный факт. Данное нововведение относится к интерполированию строк, то есть добавление константного символа не допускается:

const char a = 'a';
const string constStrFirst = "FirstStr";
const string summaryConstStr = $"SecondStr {constStrFirst} {a}";
// Error CS0133
// The expression being assigned to
// 'summaryConstStr' must be constant

Одновременное использование объявленных и инициализируемых переменных при деконструировании

В старом варианте вызова деконструктора можно использовать ИЛИ объявленные переменные (все объявлены), ИЛИ переменные, которые мы инициализируем прямо при вызове (все НЕ объявлены):

Car car = new("VAZ 2114", "Blue");

var (model, color) = car;
// Инициализация

string model = string.Empty;
string color = string.Empty;
(model, color) = car;
// Присваивание

Новая версия языка допускает одновременное использование как объявленных ранее, так и не объявленных переменных при деконструировании:

string model = string.Empty;
(model, var color) = car;
// Инициализация и присваивание

В версии C# 9 возникала ошибка:

Error CS8184: A deconstruction cannot mix declarations and expressions on the left-hand-side.

Заключение

Как говорилось ранее, список изменений не столь велик, как в версии C# 9. Одни изменения упрощают работу, а другие разблокируют ранее недоступные возможности. Развитие не стоит на месте, а мы будем ждать новых обновлений языка C#.

Если вы еще не ознакомлены с нововведениями версии C# 9, то о них мы тоже подробно рассказывали в отдельной статье.

Если вы хотите ознакомиться с первоисточником, можете прочесть документацию Microsoft.

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Valentin Prokofiev. What's new in C# 10: overview.

Комментарии (57)



  1. rsashka
    20.10.2021 11:52
    -3

    Бывает такое, что пространство имён необходимо использовать во всём файле, но это может немного сдвинуть отступы текста программы.

    А что мешает настроить отступы так, чтобы namespace не сдвигал их в тексте программы?


    1. SvyatoslavMC
      20.10.2021 11:56
      +3

      Раз это доработали, значит что-то мешает. Скорее всего, вручную сдвиги никто не трогает, т.к. в Visual Studio хороший режим автоформатирования. А настроить сдвиги для разных элементов вроде невозможно, да и будет странно выглядеть.


      1. rsashka
        20.10.2021 13:47
        -10

        Странно назвать режим автоформатирования хорошим, если он не позволяет настраивать отступы в namespace и для этого потребовалось вносить доработки аж в сам синтаксис языка. Как по мне, очень своеобразная логика :-)


    1. mayorovp
      20.10.2021 21:20
      +11

      Кроме редактора, который не настраивается таким образом?


      Наверное, мешает то, что пара фигурных скобок без отступа выглядит уродливо.


    1. lostmsu
      21.10.2021 00:28
      +1

      Спесь инженеров Microsoft и желание переусложнить всё и вся во имя собственного Эго:

      https://github.com/dotnet/roslyn/pull/38755


  1. korsetlr473
    20.10.2021 13:23
    +4

    вот эти record ктото сумел применять?

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


    1. fransua
      20.10.2021 13:38
      +10

      Да, перешли на них для DTO. Удобно клонировать и сравнивать


      1. zerg903
        21.10.2021 01:29
        +6

        Да, с record код стал проще, но насчет "сравнивать" - есть нюансы:

        private record Dto(string Name, List<int> Values);
        
        public static void Main()
        {
          var dto1 = new Dto("name", new List<int>() { 1 });
          var dto2 = new Dto("name", new List<int>() { 1 });
        
          Console.WriteLine(dto1 == dto2); // false !!!
        }

        Чуда нет… и зная .net понимаешь, почему так происходит (для ссылочных типов, если не переопределен метод Equals(), то сравнение делается по ReferenceEquals()). Но все же хотелось бы получить иной результат, раз уж везде пишут о том, как удобно сравнивать record-ы.


        1. orthoxerox
          21.10.2021 10:55
          +2

          Да, для коллекций в рекордах надо использовать свои обёртки с переопределённым сравнением. Или неизменяемые типы из System.Collections.Immutable


          1. Saladin
            21.10.2021 11:26

            А можете развернуть мысль касательно System.Collections.Immutable?
            Я сейчас быстро проверил, и похоже, что обыкновенное сравнение неизменяемых коллекций даёт такой же результат как и сравнение обычных коллекций


            var a = ImmutableList<int>.Empty.Add(1);
            var b = ImmutableList<int>.Empty.Add(1);
            Console.WriteLine(a.Equals(b)); // False


            1. orthoxerox
              25.10.2021 10:37
              +1

              Тогда скорее "завернуть мысль". Перепутал с коллекциями из language-ext.


            1. VanKrock
              28.10.2021 18:26

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


  1. anonymous
    00.00.0000 00:00


    1. egorozh
      20.10.2021 16:29
      +1

      Походу это тоже работает) По крайней мере, сейчас попробовал убрать "namespace MyApp;" в файле класса - всё собралось нормально с глобальным неймспейсом проекта.


      1. beskaravaev
        22.10.2021 12:53
        +1

        Такое и раньше работало, но так делать не стоит


        1. maxkatz6
          30.10.2021 13:37
          -1

          Почему нет? Разве не давно была пропертя в msbuild/csproj для определения дефолтного неймспейса? С приходом нового упрощённого csproj эта пропертя теперь не включена в проект и имеет значение имени проекта по умолчанию.

          Не вижу разницы, если это было бы особым ключевым словом. Делать так не стоит только потому что проще видеть явное имя.


          1. mayorovp
            30.10.2021 14:12

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


  1. anonymous
    00.00.0000 00:00


    1. Deosis
      21.10.2021 08:26
      +1

      Или автозадание namespace из структуры проекта.


    1. vabka
      21.10.2021 16:26
      +1

      А как же тогда папки с кодом делать?


  1. pankraty
    20.10.2021 21:59
    +4

    Но при наследовании есть одна особенность: переопределенный вами метод ToString не будет унаследован потомками от родительской записи. Для того чтобы потомки записей наследовали метод ToString, стало доступно использование ключевого слова sealed, которое запрещает компилятору генерировать имплементацию метода ToString у дочерних записей.

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

    Интересный факт. Данное нововведение относится к интерполированию строк, то есть добавление константного символа не допускается:

    Очень странная, ничем не обоснованная особенность. Недоделка, которую исправят в С# 11?


    1. Naf2000
      20.10.2021 23:22
      +2

      В запечатанном ToString вызовите свой виртуальный ToCustomString. Переопределяйте его по необходимости. Он то не будет автоматом переопределяться


    1. mayorovp
      20.10.2021 23:53

      Вообще-то особенность ещё как обоснованная: форматирование объектов, в общем случае, зависит от текущей культуры потока и не может быть выполнено во время компиляции, так что как ни крути — придётся вводить белый список того что может быть сделано компилятором. Сейчас в этот белый список вошли строки. Да, символы можно было включить тоже, но зачем?


      1. pankraty
        21.10.2021 08:48
        +2

        В общем-то незачем, конечно (впрочем, и без интерполированнных строк в константах можно было жить), просто выглядит неконсистентно. Это как разрешить операцию сложения только для многозначных чисел, но не для однозначных


  1. mvv-rus
    20.10.2021 22:05
    +2

    Мое личное мнение: пара полезных семантических вещей, которых раньше не хватало, и которые удобно реализовать было нельзя: это инициализация элеметов структур и конструктор стурктуры по умолчанию…

    … и толстый слой молочного шоколада синтаксического сахара.


  1. NeoCode
    20.10.2021 22:38

    А «одновременное использование объявленных и инициализируемых переменных при деконструировании» в операторе switch еще не ввели?

    По идее это как раз нужно для полноценного паттерн матчинга по составным объектам (кортежам и т.п.), когда в case-паттерне задается часть полей, а другая часть объявляется как новые переменные. Если заданные поля совпадают с соответствующими полями аргумента switch, то поля, объявленные как переменные, инициализируются соответствующими полями из аргумента switch.


  1. Naf2000
    20.10.2021 23:24

    Расширять работу с expression планируют? Например with в лямбдах?


    1. AgentFire
      23.10.2021 00:27

      а поподробнее пример-пожелание можно?


      1. Naf2000
        30.10.2021 13:30

        Сколько я понимаю, выражение копирование с with https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/operators/with-expression недоступно в expression


  1. FreddyFox
    21.10.2021 01:24
    +2

    Хорошие нововведения и конкурсы интересные.


  1. ArchiDNA
    21.10.2021 01:26
    +1

    Да исключите наконец слово new из создания объекта. Точнее сделайте его факультативным.


    1. pankraty
      21.10.2021 09:18
      +1

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

      Пример (абсолютно синтетический):

      private MyClass MyClass(long x) => new MyClass((int)x+1);

      var p = MyClass(1) ;

      Сейчас вызывается метод, а если отказаться от new - будет вызываться конструктор, как более подходящая перегрузка.


      1. pankraty
        21.10.2021 09:33
        +2

        Хотел написать, что для анонимных объектов это могло бы сработать, но там могут быть неприятные эффекты, когда блок кода с присвоением переменной из-за неправильно поставленной точки с запятой будет трактоваться как создание анонимного объекта без присваивания куда-то.

        { x = 1 };

        { x = 1; }


      1. marshinov
        31.10.2021 15:55

        В Котлин пришлось object сделать ключевым словом, чтобы new убить. Хотя там конечно специфика JVM еще, например functionName ClassName - сразу видно где конструктор, где функция. Ну и сама среда немного другое разрешает.


  1. orthoxerox
    21.10.2021 10:59
    +1

    Самое интересное в C#10 - абстрактные статические методы в интерфейсах, но они пока в preview, чтобы Микрософт смог собрать обратную связь и зарелизить их в C#11 с учётом пожеланий программистов. Так что пользуйтесь ими в своих любительских проектах и жалуйтесь.


    1. marshinov
      31.10.2021 15:50

      А я правильно помню, что в .NET 6 опцию можно включить и фича появляется?


  1. khabib
    21.10.2021 11:27
    +3

    Смотрю на нововведения, как они читаются/выглядят, и сравниваю новинками C++. Плюсы делают реально инопланетяне.


  1. KaiOvas
    21.10.2021 16:21
    -4

    Нововведения ниочем.

    Последнее существенное - record все остальное это просто присыпка сахаром и побрызгивание дезодорантом.... Язык по сложности скатывается к C++ но конечно до этого еще далеко, хотя тенденцию видно.


    1. vabka
      21.10.2021 16:30
      +2

      Всегда можно попробовать F# :)


      1. KaiOvas
        21.10.2021 17:38
        +1

        Это было бы прекрасно если бы этот язык продвигали в продакшен но к сожалению найти вакансию с C# гораздо легче чем с F# несмотря на то что после F# обратно уже не хочется. Я использую F# для своих внутренних утилит но последний раз когда я попытался исходники утилиты написанную на нем залить в репозиторий то выслушал много идиоматических выражений от кастомера что я "исказил и обфусцировал исходники С#".


        1. marshinov
          31.10.2021 15:49

          На самом деле не так уж и больно с F# на C# возвращаться. С Котлин на Java больнее:)


  1. d_ilyich
    21.10.2021 21:08

    Прошу прощения, что не совсем по теме. Посоветуйте, пожалуйста, актуальную литературу/курсы по C# (.NET). Желательно на русском, хотя бы для начала. Сейчас пока по сайту Метанит осваиваю.


    1. SvyatoslavMC
      22.10.2021 10:34
      +2

      Если Вы находитесь на начальном уровне, Вам подойдёт любая книга, где освещены все возможности языка, начиная с базовых. Обычно они очень толстые. Берите самое свежее издание, какое найдёте. С ростом опыта подход к обучению надо изменить, но пока так.


    1. VanKrock
      28.10.2021 18:37
      +1

      Советую еще посмотреть курсы на ulearn от УрФУ, по моему один из лучших курсов по C# для начинающих


    1. marshinov
      01.11.2021 15:23

      Не читайте "любую", читайте эту. И http://ulearn.me.


  1. SWATOPLUS
    22.10.2021 17:11
    +2

    Required properties не добавили, расходимся.


    1. beskaravaev
      23.10.2021 10:44
      +1

      Можно небольшое описание того, что это, для чего и как должно работать? Заранее спасибо.


      1. SWATOPLUS
        23.10.2021 20:45
        +1

        Это киллер фича, которая обязывает инициализировать указанные свойства в DTO.

        Казалось бы есть конструктор для этого, но систаксис с инициализатором свойств намного удобнее. Это ещё один шаг в борьбе с NullReferreceException.

        https://github.com/dotnet/csharplang/blob/main/proposals/required-members.md

        https://github.com/dotnet/csharplang/issues/3630

        И в догонку Simplified parameters null validation. Которое гарантирует null-безапасность и пишет бойлерплейт за вас.

        // Before
        void Insert(string s) {
          if (s is null)
            throw new ArgumentNullException(nameof(s));
          ...
        }
        
        // After
        void Insert(string s!) {
          ...
        }

        https://github.com/dotnet/csharplang/issues/2145


        1. SvyatoslavMC
          24.10.2021 19:57
          +2

          В оригинальном репозитории богатый список киллер фич. Абсолютно без сарказма. Но из предложений в реальные обновления попадают единицы.


          1. marshinov
            31.10.2021 15:57

            Добавляют потихоньку. Лучше медленно, но в верном направлении.


  1. marshinov
    31.10.2021 15:47

    Жалко Static abstract members in interfaces только в preview.


    1. SvyatoslavMC
      01.11.2021 15:11

      Как я понял, это только пропозал. До превью там ещё далеко.


      1. marshinov
        01.11.2021 15:21

        Мне казалось, что они ее в preview-режиме выпустят. Но могу ошибаться.


    1. mvv-rus
      01.11.2021 18:02

      А зачем static нужны именно в интефейсах?
      IMHO им место в ограничениях для класса, в where.

      Интерфейс (точнее — ссылка на него) — это то, к чему можно привести (ссылку на) экземпляр типа, и вызвать его экземплярные методы как виртуальные (AKA через позднее связывание), т.е. — некая дополнительная виртуальная таблица, котоорая может присутвовать во многих классах. А статические методы экземпляров не требуют.

      Тем более, что ограничения по наличию методов, реализованных в классе, в языке уже реально присутствуют (например — ограничения на то, что стоит после оператора await). Так что IMHO лучше бы их включить в синтаксис в явном виде (вроде, было когда-то такое предложение, что с ним сейчас — не в курсе) И ограничения на обязательность реализации статических методов войдут туда вполне органично.


      1. marshinov
        01.11.2021 18:20

        Они рассматривали несколько вариантов, в итоге решили, что проще разрешить статику в интерфейсах, чем вводить дополнительные синтаксические сахарюшки. Т.е. если в generic'е хочется +, то нужно объявить:

        Add<T>(T x, T y) where T: IAddable 
        
        interface IAddable { static + }

        В proposal'е это коротко написано. Если другие proposal'ы посмотреть, то че только не предлагали: начиная с тайпклассов, заканчивая трейтами. ИМХО, у C# прослеживается довольно выигрышная стратегия вводить хорошие частные решения для типовых задач. Когда Бреслав говорит, что "у нас async/await - это не ключевые слова, а функции, а ключевое слово у нас suspend" я не вполне уверен, что это преимущество Котлина, потому что чтобы использовать async/await без понимания внутренностей достаточно не использовать .Result и всегда предпочитать асинхронные версии методов и не оборачивать синхронные в Task.Run. С корутинами все несколько сложнее на мой вкус...


        1. mvv-rus
          01.11.2021 20:48
          -1

          Они рассматривали несколько вариантов, в итоге решили

          Ну, решать-то, конечно. Им, но вот «особенности», которые в общую схему языка не вписываются — типа await, выражение после которого должно просто иметь в описании некоторые буковки — один интерфейс и пару методов с фиксироваными названиями — я считаю, неплохо было бы таки вписать в общую схему языка.
          Такая же фича есть и в LINQ query syntax — типа, что стоит во множественных from, должно реализовывать SelectMany с определенными параметрами — а там оно может быть что угодно. Но там хоть язык формально другой, отличающийся от основного массива C#
          Тут проблема IMHO в том, что такие вот исключения из общих правил делают язык громоздким. Чем их больше — тем более громоздким он получается. А это — повышение требований к квалификации разработчика — при том, что индустрия сейчас и так страдает от завышенных требований, т.е. нехватки достаточно кавлифицированного персонала.
          Но, конечно, пусть они делают там что хотят.