Pull to refresh

Comments 187

public override int GetHashCode()
{
   return (Damage.GetHashCode() * 17 + Durability.GetHashCode());
}

А почему умножение именно на 17? А что будет если параметров три? Четыре?

Просто один из хороших универсальных вариантов хэширования. В случае большего количества параметров, я думаю, будут поступать примерно так, как рекомендуется в ответе Джона Скита на вопрос: stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode/263416#263416
Это просто пример из предложения по Records. В реальной реализации скорее всего будет использоваться код комбинирования хэшкодов из Tuple / ValueTuple, который поддается оптимизации лучше, чему умножение, и который дает меньше коллизий на больших числах:

// RyuJIT optimizes this to use the ROL instruction
// Related GitHub pull request: dotnet/coreclr#1830
 uint rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27);
return ((int)rol5 + h1) ^ h2;

using можно рассматривать как альтернативу typedef в рамках файла
Это слишком ограниченная альтернатива (именно из-за ограниченности одним файлом). А так — да, использую помаленьку, где это возможно и к месту.
Зачем вводить асинхронные последовательности, если observable — это, по сути, они и есть?
Рекорды, nullable reference и switch как выражение — огонь, в Котлин очень удобно использовать )

IObservable — push-based stream
async IEnumerable — pull-based stream

Все же дефолтные реализации в интерфейсах это спорный момент. В Java-мире, например, не очень вводушевленно приняли это фичу (хотя пример так себе, там и с var боролись).

Так не обязательно же эту фичу всюду сувать. Есть место где она очень даже нужна — это LINQ. Просто посмотрите что там сейчас творится.


Раз: https://github.com/Microsoft/referencesource/blob/master/System.Core/System/Linq/Enumerable.cs#L38


        public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector) {
            if (source == null) throw Error.ArgumentNull("source");
            if (selector == null) throw Error.ArgumentNull("selector");
            if (source is Iterator<TSource>)
                return ((Iterator<TSource>)source).Select(selector);
            if (source is TSource[])
                return new WhereSelectArrayIterator<TSource, TResult>((TSource[])source, null, selector);
            if (source is List<TSource>)
                return new WhereSelectListIterator<TSource, TResult>((List<TSource>)source, null, selector);
            return new WhereSelectEnumerableIterator<TSource, TResult>(source, null, selector);
        }

Два: https://github.com/Microsoft/referencesource/blob/master/System.Core/System/Linq/Enumerable.cs#L1314


        public static int Count<TSource>(this IEnumerable<TSource> source) {
            if (source == null) throw Error.ArgumentNull("source");
            ICollection<TSource> collectionoft = source as ICollection<TSource>;
            if (collectionoft != null) return collectionoft.Count;
            ICollection collection = source as ICollection;
            if (collection != null) return collection.Count;
            int count = 0;
            using (IEnumerator<TSource> e = source.GetEnumerator()) {
                checked {
                    while (e.MoveNext()) count++;
                }
            }
            return count;
        }

Метод Count, кстати, является наглядным примером основного недостатка такого подхода: туда автор кода не включил проверку на IReadOnlyCollection, и теперь всем остальным можно с этим только смириться, расширяемость-то нулевая.


Все эти методы изначально должны были быть методами интерфейса с реализацией по умолчанию.

Еще из приятностей — планируют сделать switch как expression, то есть что-то вроде такого (пример с MS Build 2018)

static string M(Person person)
{
    return person switch
    {
        Professor p => $"Dr. {p.LastName}, Professor of {p.Subject}",
        Student s => $"{s.FirstName}, Student of {s.Advisor.LastName}",
        Person p when p.LastName == "Campbell" => $"Hi, {p.FirstName}",
        _ => "Come back next year!"
    }
}
Блин, это же есть в статье, извиняюсь.

Но у C# есть хорошая база – Common Language Infrastructure (CLI). CLI дает возможность написать новый язык с чистого листа, оставив при этом возможность работы со старым кодом, который еще долго может оставаться в рабочем состоянии. В качестве ближайшего аналога можно рассмотреть ситуацию с Java/Kotlin. Возможно, Майкрософт пойдет таким путем?

Наверное несбыточная мечта, но очень хотелось бы, чтобы Майкрософт взялись за развитие Nemerle, он был разработан 15 лет назад и до сих пор уделывает по фичам почти все современные языки.
В 2012 году JetBrains взяло Nemerle под свое крыло для того, чтобы выпустить N2 — язык, который должен был стать развитием Nemerle.

Но с тех пор ничего интересного это сотрудничество для .Net комьюнити не принесло.
Небольшое уточнение — команда Nemerle разрабатывала в JetBrains фреймворк Nitra для создания различных DSL-ей. К сожалению, JetBrains расстались с командой Nemerle в 2016 году, все прошло очень тихо и незаметно для большинства. Исходя из информации, которую можно вынести из обсуждения на рсдн, ситуация была примерно такая — ребята в компании были предоставлены сами себе, их практически не контролировали, при этом никто толком не мог объяснить (ни менеджеры, ни сами разработчики), какие именно цели сможет решать фреймворк и зачем он вообще нужен. В это же время разрабатывался Kotlin и один из инициаторов проекта заинтересовался им больше (тоже в контексте DSL, если не ошибаюсь) и вышел из проекта. Ребята остались совсем одни и компания совершенно не знала, что с ними делать, так что это закономерно окончилось закрытием проекта. Это то, как я понял ситуацию и конечно могу ошибаться.

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

Меня очень впечатлил вот этот доклад на дотнексте по АОП в дотнете (собственно, там я впервые услышал о немерле). Там, где сейчас используются фреймворки типа PostSharp, оказывается можно обойтись встроенными средствами языка. Вот если будет больше таких хороших примеров использования немерле, которые показывают реальную практическую пользу — то у языка есть шансы добиться признания.

Выглядит это направление (макросы + DSL без всяких костылей) очень интересным и хочется, чтобы это это вылилось в нечто завершенное.
Для DSL есть вполне живой MPS.
По этому поводу есть отрывок интересного диалога с главным разработчиком нитры и немерле на рсдне:

Так Нитра сравнивается с Решарпером? Или с Котлином? Или MPS? Или со всем тремя? Как бы то ни было, Нитре надо было обосновать свою значимость с теми проектами, что у компании уже есть.

Сравнивалась. И с MPS сравнивалась. Как раз опыт применения и «продажи» MPS сыграл плохую службу. Многие в конторе были разочарованы в MPS. Ранее MPS применяли для внутренней разработки, но получили от его применения больше пролем, чем пользы. В итоге в некоторых частях от его использования отказались и остались довольны этим. Так как концептуально с MPS у Нитры много общего было, скепсис по отношению к MPS перекинулся и на Нитру. Я обращался к тому кто придумал и поддерживает MPS (это один из владельцев фирмы), но он даже не ответил на мое письмо. И это при том, что программисты из группы MPS были очень заинтересованы нашим докладом и очень печалились по поводу принятого решения.
Неужели у MPS не нашлось хорошего евангелиста? Понятно, что инструмент весьма специфичный, да ещё и со сравнительно высоким порогом вхождения, но в некоторых отраслях вполне мог бы «выстрелить».
Они до сих пор не поняли что у них ключевое слово private — лишнее.
Надо к not null добавить что-то вроде "!!" из Kotlin — «да, я знаю, что я делаю, разреши использовать nullalbe».

Как сказал Андрей Бреслав, это не "разреши", а "отвали" :)

И "??" — Типа пофиг возвращай null
Да, в превью такое есть. Только один восклицательный знак, а не два.
Все еще не решено, будет ли оператор диапазона включающим или исключающим, т.е., будет ли результирующий диапазон включать значение конец. Возможно, будут доступны оба синтаксиса

Мне казалось, что вот тут (55 минута)


msbuild 2018

довольно доходчиво рассказали про System.Index.


Например, 5..8 эквивалентно последовательности 5, 6, 7, т.е. аналогично стандартному индексированию zero-based массивов.


1..^1 индесирует элементы с 1 по n-2, т.е. выкидывает один в начале и один в конце. 0..^0 эквивалентно последовательности с 0 по n (не включая n), т.е. индексирует всю коллекцию. ^0 по сути эквивалентен длинне последовательности.


Доступны также варианты n.., ..m и .., которые эквивалентны чему-то вроде n..^0, 0..m, 0..^0, если я ничего не напутал.


Источник (кроме видео) здесь, на гитхабе

Да, обязательно всем посмотрите видео с Build2018 из комментария выше! Хотя оно показывает фичи не только будущего C#8, но и переходные C#7.1-7.3, часть из которых уже доступны для использования. Там гораздо больше интересного, чем в этом переводе. Такие вещи как ref и readonly переменные, in параметры, extension методы на ref переменных, и возвращающие ref переменные методы. Все это позволяет такие вещи писать, которые отстрелят вам обе ноги. Тот же тип Span<> используемый мельком в коде статьи, но не все о нем до сих пор знают, благодаря ref переменным мы получаем возможность присваивания в индекс без копирования элементов, что, честно, хочется опробовать на многопоточном коде, т.к. утверждается что Span является прямым указателем в память. Можно даже просто выделить Span на текущем стеке через stackalloc без unsafe, т.к. это окно в память внутри управляемого кода.
Nullable Reference Types
IWeapon? canBeNull;
IWeapon cantBeNull;

Отличная идея — сломать обратную совместимость по синтаксису.
Похожа на идею — значение True равно 0. Во где был фееричный треш на унаследованном коде.

Default Interface Methods

Отличная идея, только кофеварку забыли приделать и пемзу для пяток…
В отличие от множественного наследования, данный функционал позволяет избежать проблемы ромбовидного наследования, при которой метод с одним и тем же именем определен в нескольких интерфейсах. Для этого C# 8.0 требует, чтобы каждый класс и интерфейс имели наиболее специфическое переопределение каждого унаследованного члена:

В С++ уже лет 16 назад это не было проблемой. А в .Net это объяили «Злом», предали анафеме, а сейчас впихивают обратно. Правда почему-то ректально…

P.S.
Что они курят…
Ну в C# постоянно синтаксис изменяется и обратно он несовместим, странно иметь новые языковые фичи которые в зависимости от версии окружения по разному бы читались. В конкретном случае это не имеет отношения к Nullable структуре, а лишь на этапе компиляции помогает явно указать для компилятора, чтобы тот мог в дальнейшем подсказывать где и когда можно пропустить null и поиметь nullReferenceException.
А насчет методов в интерфейсах — пример приведен явно неудачный, и в видео с build 2018 в последних 6 минутах более адекватный пример:
interface ILogger
{
    void Log(LogLevel level,string message);
    void Log(Exception ex)=> Log(LogLevel.Error, ex.ToString());
}

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

Чтобы иметь возможность переопределить.

Ну в C# постоянно синтаксис изменяется и обратно он несовместим, странно иметь новые языковые фичи которые в зависимости от версии окружения по разному бы читались.

Всегда декларировалось, что интерфейс это декларация-контракт взаимодействия. Реализация — этим занимаются другие сущности.
А тут предлагают «улучшить» эти соглашения, только потому что кто-то «убил» множественное наследование классов, потому что это «будет сложно».
Может просто разрешить множественно наследование классов, чем так извращаться, причем ломая привычные соглашения?
или это очередной Vendor lock в исполнении M$?
В документации часто указываются примеры реализаций. Не вижу ничего страшного в том, что теперь их можно указывать еще и в коде.
Причем тут «пример использования кода в документации»? Почему бы уже тогда к интерфейсу не приделать конструктор с деструктором, если уж пошла такая пьянка.
Отличная идея — сломать обратную совместимость по синтаксису.

А где конкретно она ломается-то, если учесть, что это opt-in?

Может быть тут?
IWeapon cantBeNull;

Проблема такого изменения в том, что мы прерываем совместимость со старой кодовой базой: Здесь предполагается, что все переменные в старом коде по умолчанию не могут содержать null. Чтобы справиться с этой ситуацией, статические анализаторы компилятора на null-безопасность должны быть выборочно включены в зависимости от проекта.


У вас 15-летний код, и тут вдруг «Ай, ваш код шлак, у вас может быть null значение, срочно нужно переколбасить ваш код (вы же идиот и до сих пор об этом не подумали) или выключите нафиг наши предупреждения».
Нормальная адекватная логика.
Да и логика «не могут содержать null» подразумевает, что чем-то должна быть инициализирована переменная по умолчанию? Чем?
У вас 15-летний код, и тут вдруг «Ай, ваш код шлак, у вас может быть null значение, срочно нужно переколбасить ваш код (вы же идиот и до сих пор об этом не подумали) или выключите нафиг наши предупреждения».

Подождите, но зачем вы включали эти предупреждения?


Да и логика «не могут содержать null» подразумевает, что чем-то должна быть инициализирована переменная по умолчанию? Чем?

Допустимым значением.

Подождите, но зачем вы включали эти предупреждения?

Открываем исходник и фиг понимем «оно null или не null? А opt включена или нет ?»
Это же «очевидно»… или нет?

Допустимым значением.

Это каким например?
Создаем экземпляр класса, в конструкторе по умолчанию «ничего нет», можно провести «ленивую инициализацию» — но у нас же «null» не может быть? А что тогда? Создавать по всему коду экземпляры классов только что бы «ублажить» компилятор?

Сама по себе фича полезная. Code contracts отлично ловили такие ошибки на этапе компиляции. Но реализация — выглядит более чем сомнительно.
По стандарту языка — ссылочный тип или null или «что-то есть».
Нафига городить на уровне языка проверку «рук от бедер», тем более что сейчас компилятор не пропускает не проинициализированные переменные?
Открываем исходник и фиг понимем «оно null или не null? А opt включена или нет ?»

Ну да, печаль. Но это уже не проблема обратной совместимости.


Это каким например?

Допустимым.


Создаем экземпляр класса, в конструкторе по умолчанию «ничего нет», можно провести «ленивую инициализацию» — но у нас же «null» не может быть? А что тогда?

А тогда размечаем поле как nullable, в чем проблема-то?


тем более что сейчас компилятор не пропускает не проинициализированные переменные?

Куда не пропускает? То, что поля автоматически инициализируются, вы помните?

Куда не пропускает? То, что поля автоматически инициализируются, вы помните?

Я то помню, а вот компилятор похоже забыл об этом :)
...
ITest interfaceXXX;
...
interfaceXXX.FN();

Error CS0165 Use of unassigned local variable 'interfaceXXX'



Это каким например?
Допустимым.

А конкретнее? null? a default для ссылочных типов случаем не null ?!
Или сделаем еще один тип nullnull или IAmSureNull? :)
Я то помню, а вот компилятор похоже забыл об этом

Не надо путать переменные и поля.

Ok. Добавим.
...
Warning CS0649 Field 'Program._interfaceXXX' is never assigned to, and will always have its default value null.

Смысл то не в полях /переменных, а в том, что уже достаточно инструментальных средств что бы не выстрелить себе в ногу.
На мой взгляд.

Смысл то не в полях /переменных, а в том, что уже достаточно инструментальных средств что бы не выстрелить себе в ногу.
На мой взгляд.

Вам — достаточно. А я устал каждый раз смотреть, может ли метод вернуть null.

Создаем экземпляр класса, в конструкторе по умолчанию «ничего нет», можно провести «ленивую инициализацию» — но у нас же «null» не может быть? А что тогда?

Это вообще как? Можете привести пример кода?

пример — DI типовой. Как тут быть?

Вот как раз в "типовом DI" вам не могут прийти снаружи null-значения сервисов, и поля с ними тоже после конструктора не могут быть null. И в чем проблема?

Вот как раз в «типовом DI» вам не могут прийти снаружи null-значения сервисов, и поля с ними тоже после конструктора не могут быть null.

В идеале да, не должны.
ITest _testXXX;
class ZZZ(ITest value)
{
_testXXX.Fn(); <-- банальная опечатка
_testXXX = value;
}


А между созданием и инициализацией в каком состоянии будет? какая-то религия мне запрещает написать кривой код и в конструкторе накосячить, дернув не проинициализированый интерфейс?
Наверняка будет предупреждение вроде «nullable field used before initialized». Если не будет — я на них обижусь :-)

Прямо сейчас (на шарплабе, версия от 12 мая) не показывается.

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


Что забавно, если поле объявить как nullable, warning — и правильный! — показывается.

Подождите, но зачем вы включали эти предупреждения?


А ты наивно думаешь, что это так и останется опцией?? Такой смелый, но бестолковый шаг, как ломка совместимости (причём фундаментальная такая), не пройдёт на уровне опций — она вывернет наизнанку всю работу, где старые проекты сосуществуют с новыми — поди, разберись, где там «новый C#», а где опции не нужны! Чувствуешь, попахивает говнецом? Вот-вот! И всё ради одной ничтожной цели — чтобы каждая обезьяна из Индии сразу получала по лапам, используя ссылки. Ну так может не коверкать язык, а нанять специального же индуса, который это будет делать физически????

12 лет я пишу на C# и ловил NRE не раз, но не настолько раз, чтобы похабить язык, да ещё и мой код проверками «а не null ли вы мне подсунули?». Идиотизм чистой воды. Для(против) NRE есть статические анализаторы и голова. Если головы нет — никакие крючкотворства с языком не помогут.

Lofer прав, ТАКИЕ изменения в языке нафик не нужны! От этих «благих намерений» тошнит.

PS
Почему бы индусне не сделать собственный язык, с песнями и танцами?!!! Вот там пусть хоть утыкают всё проверками! Basic Indian edition! :))
Для(против) NRE есть статические анализаторы и голова.

  1. Зачем решать самому задачи, которые прекрасно решаются автоматически, учитывая, что человеческое время дороже машинного?
  2. Система типов — это и есть статический анализатор, встроенный язык. В чем проблема?
  3. Если ваш код и так правильный, то и варнингов не будет. А если неправильный — то ворнинг укажет место бага, которое надо будет исправить. Что тут плохого?

И всё ради одной ничтожной цели

Это не ничтожная цель, а исправление (пусть и запоздалое) billion dollar mistake. Впрочем, куда там безголовой обезьянке Энтони Хоару до хаброикспертов!

А ты наивно думаешь, что это так и останется опцией?

В ближайшей мажорной версии — да.


12 лет я пишу на C# и ловил NRE не раз, но не настолько раз, чтобы похабить язык, да ещё и мой код проверками «а не null ли вы мне подсунули?»

То есть вы null guards не используете?


Для(против) NRE есть статические анализаторы и голова.

Ну так C# 8 и добавляет такой анализатор.


Lofer прав, ТАКИЕ изменения в языке нафик не нужны!

Прекрасно. Не нужны — не используйте. Оставайтесь на C# 7, или более ранних версиях, или вообще уходите на другой язык.

Вопрос тут больше, почему так сразу радикально?
Что мешало дать расставить атрибут [Notnull] там, где он адекватен, и посмотреть уровень его использования?
> Что мешало дать расставить атрибут [Notnull] там, где он адекватен, и посмотреть уровень его использования?

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

Зависит от задачи. В моих null в какой-то ссылке это очень типичная ситуация, а где жёстко не допускается — гарантируется, например, тем, что метод объекта нельзя вызвать без самого объекта.

> Логично, что квалификатор должен стоять в случае исключительной ситуации а не наоборот.

Логично, что столь радикальное воздействие лучше делать плавно. Комбинация атрибута уровня входного файла (с дефолтом nullable) в первой версии и атрибутов отдельных переменных дала бы достаточно средств для плавного введения там, где оно действительно нужно.
Зависит от задачи.

Да нет, не зависит. null практически никогда не нужен. 90% случаев его использования — либо code smell из-за лени, либо ошибка.


В моих null в какой-то ссылке это очень типичная ситуация,

И вы везде аргументы на нулл проверяете, ведь правда?


Логично, что столь радикальное воздействие лучше делать плавно.

Они и делают плавно, начиная с ворнингов. Кроме того, ваше предложение точно так же ломает обратную совместимость, т.к. вы не сможете засунуть ваш дефолтный nullable в ф-ю, которая ждет non-nullable аргумент (и, понятно, стандартная библиотека будет переписана так, что все ф-и, кроме тех, где это необходимо, будут принимать non-nullable).


Вообще это все не проблема. Реальная проблема — отсутствие фильтров в системе типов. Но вот это почему-то не обсуждается.

И вы везде аргументы на нулл проверяете, ведь правда?

Надеюсь это был сарказм? :)
Да в общем-то в ISO 2700х, в части разработки безопасного и качественного, кода проверка аргументов это обязательное действие. Иначе с аудитом качества могут быть проблемы.
Медленно, но хорошо работающий код, лучше быстрого, но не работающего.

P.S.
Недавно приятель рассказывал историю что с ним произошла.
Пошел сдавать анализы. По результатам анализов на принудительно лечение его там же чуть не отправили. Хорошо у врача хватило мозгов сравнить с предыдущими анализами, и понять что программа глюконула.
Не удивлюсь, если кто-то сэкономил на проверке аргументов.
Не удивлюсь, если кто-то сэкономил на проверке аргументов.

Вот чтобы такого не было, и нужны non-nullable типы :)

Ну ок. Допустим добавим. Будут пихать туда всякую дрянь, как и пихали, без всяких проверок.
Мозги нужны и руки не от бедер. .Net и делали «безопасным». Уже 15 лет все безопаснее и безопаснее. Глядя на последние улучшения, так и хочется спросить «И сильно помогло? перебороли идиотизм ?» Похоже что нет, а вот порог вхождения понизили значительно.
Такое впечатление, что больше «безопастности» — больше говнокода — …
Будут пихать туда всякую дрянь, как и пихали, без всяких проверок.

Ну так если напихают — то будет ворнинг. А потом и вовсе ошибка компиляции, когда-нибудь.


Глядя на последние улучшения, так и хочется спросить «И сильно помогло? перебороли идиотизм ?»

Как часто у вас в .net segmenation fault возникал? А за границу массива с попаданием в соседний объект сколько раз выходили?

> проверка аргументов это обязательное действие.

Ну вообще-то, если для поступающего аргумента ссылочного типа рантайм гарантирует, что ссылка не null, потому что это проверено где-то раньше, то такая явная проверка ещё раз просто не нужна.
В этом действительно смысл всех ограничений системы типов: где-то это явный notnull, где-то умолчательный notnull при возможности делать Nullable или Optional, где-то возможность писать «type Temperature = new Integer range -273..+300», как в Ada, и ещё много разных вариантов.
И если ограничение выполнено на «входе» в соответствующий тип раньше данной функции, блока кода или конкретного предложения — то проверять ещё раз на месте… по-моему, смысла мало — это уже super-defensive programming получается.

Но вот что мне не нравится — это то, что с одним единственным вариантом nullable ссылок носятся, как с писаной торбой, а множество других — например, а не подсунули ли хрень не того типа просто потому, что null нельзя передавать — не делается. А это, в отличие от NPE, не даст исключение.
А как вы себе представляете ситуацию «подсунули хрень не того типа просто потому что null нельзя передавать»? Как вообще можно подсунуть что-то не того типа?
1. Через общий базовый тип.
2. Может быть и того же типа, просто не ту, что надо. Нельзя передавать null — подсунем new T(), неважно, что его пытаются под что-то использовать. Я уже такое наблюдал в реале, и ловить ошибки после этого было в разы сложнее, чем при явном NPE.
Нельзя передавать null — подсунем new T()

Ф-я-то корректно отработает, в чем проблема тогда?

Корректно — только с её точки зрения. А общая работа будет сделана неправильно.
«К пуговицам претензии есть?» ©
А общая работа будет сделана неправильно.

Очевидно, проверить, чтобы ваша программа делала что нужно компилятор не может.

Вот потому и оказывается, что срочное форсирование notnull нанесёт кучу вреда.
Как вообще можно подсунуть что-то не того типа?

Интерфейсов хватит и кривой инициализации.
Были прецеденты, насчет типа Object, после чего сделали generics & where, что бы отловить хотя бы что-то на уровне компилятора.
Дурное дело не хитрое.
> Да нет, не зависит. null практически никогда не нужен. 90% случаев его использования — либо code smell из-за лени, либо ошибка.

Жду доказательств. Причём универсальных, не зависящих от специфики задачи.

> Кроме того, ваше предложение точно так же ломает обратную совместимость, т.к. вы не сможете засунуть ваш дефолтный nullable в ф-ю, которая ждет non-nullable аргумент

Смогу. Явно проверив, что он не null. Слава богам, опознать, что в данной ветке исполнения эта проверка выполнена, компиляторы в состоянии уже лет 20. А можно попросить и сам компилятор вставлять явные проверки. Есть места, где это уже сделано.

> Они и делают плавно, начиная с ворнингов.

Плавно — это если бы они в первой версии по умолчанию не включались.

> Кроме того, ваше предложение точно так же ломает обратную совместимость, т.к. вы не сможете засунуть ваш дефолтный nullable в ф-ю, которая ждет non-nullable аргумент

Если я установлю на функции notnull на аргумент, это будет, да, само по себе переломом обратной совместимости.

> (и, понятно, стандартная библиотека будет переписана так, что все ф-и, кроме тех, где это необходимо, будут принимать non-nullable)

Понятно, что будет сломана совместимость? Радикально, да.

> Реальная проблема — отсутствие фильтров в системе типов. Но вот это почему-то не обсуждается.

Тут не обсуждается, или и в профильных форумах тоже?
Плавно — это если бы они в первой версии по умолчанию не включались.

А что, включаются? Это для меня новость.

Если я правильно прочёл исходную статью — да, включаются.

Вы прочитали ее неправильно: "The developer could opt-in to the nullability checking when he/she is ready to deal with the resulting warnings".

Жду доказательств.

Необходимость использования nullable типа на самом деле очень легко проверяется — оно действительно необходимо только тогда, если при получении null ваш метод корректно отрабатывает, возвращая осмысленный результат. У вас это действительно так, или все же большей частью при получении null — либо эксцепшен, либо возврат из метода того же null/null object?


Причём универсальных, не зависящих от специфики задачи.

От специфики задачи это зависеть не может, потому что использование или не использование nullable — фактически, способ оформления кода, а не свойство алгоритма. Можно что угодно написать лдибо так, либо эдак.


Смогу. Явно проверив, что он не null.

Да, но вам для этого надо проверяющий код написать. Что заведомо больше, чем проставить? у типа.


Слава богам, опознать, что в данной ветке исполнения эта проверка выполнена, компиляторы в состоянии уже лет 20.

Ну, какие-то компиляторы, действительно, могут. Вопрос в том еще — насколько хорошо. Вот компилятор c# будет с этой задачей справляться не слишком хорошо.


Плавно — это если бы они в первой версии по умолчанию не включались.

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


Понятно, что будет сломана совместимость? Радикально, да.

Да, будет.

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

Да.

> Вот компилятор c# будет с этой задачей справляться не слишком хорошо.

Почему? Это ведь простейший тип анализа.

> Это же ворнинги, а не ошибки. В чем проблема?

Поломка билда везде, где выставлено «не более K варнингов на компиляцию данной хери». Засорение выдачи, причём негранулируемое.
Почему? Это ведь простейший тип анализа.

Он простейший в простейших кейзах, когда вы просто делаете тест на налл в условии. Что, если у вас кастомная ф-я, проверяющая налл или подтип внутри себя? Что, если проверка на налл происходит внутри лямбды при фильтрации коллекции?


Поломка билда везде, где выставлено «не более K варнингов на компиляцию данной хери». Засорение выдачи, причём негранулируемое.

Ну и хорошо, как по мне.

> Что, если у вас кастомная ф-я, проверяющая налл или подтип внутри себя?

Такую проверку он не обязан выполнять.

> Что, если проверка на налл происходит внутри лямбды при фильтрации коллекции?

Это вопрос notnull/nullable на элементах коллекции. Его решить сложнее, да.
> Такую проверку он не обязан выполнять.

По-хорошему — вполне себе обязан.

> Это вопрос notnull/nullable на элементах коллекции. Его решить сложнее, да.

И не только на коллекциях, а на всех типах конда * -> *.
Что мешало дать расставить атрибут [Notnull] там, где он адекватен, и посмотреть уровень его использования?

То, что его придется ставить практически везде (если посмотреть на количество null guards на аргументах), а это бессмысленная работа. И то, что это будет неконсистентно с int?.

> То, что его придется ставить практически везде

Не воспроизводится.

> И то, что это будет неконсистентно с int?..

Можно подробнее?
Можно подробнее?

Ну int у вас по дефолту non-nullable и чтобы он был nullable вы ставите?.. Почему для reference-типов должно быть все наоборот?

Не воспроизводится.

У меня воспроизводится.


Можно подробнее?

int someInt; //non-nullable
int? someOtherInt; //nullable
object! someOtherObject; //non-nullable?
object someObject; //nullable, WTF?!
Насколько я понял, Default Interface Methods будет также применяться для пропертей и индексеров. Возможно это поможет наконец-то отнаследовать IList<T> от IReadOnlyList<T>.
Выглядит красиво, пока мы не вспоминаем про многозадачное окружение – как быть там в случае, если кто-то вклинился между проверкой и использованием?

Каким образом эта фича отменяет использование примитивов синхронизации? Как быть — писать корректный код, использовав один множества инструментов языка и фреймворка.

Когда же добавят Extended Properties…

Выглядит красиво, пока мы не вспоминаем про многозадачное окружение – как быть там в случае, если кто-то вклинился между проверкой и использованием?

Вот тут с переводчиком не соглашусь. Это вовсе не та проблема, которую пытаются решать. Этот синтаксис должен решать только проблему контракта, и какой-никакой автоматической его проверки (точнее это должны делать анализаторы). Этот синтаксис не будет сам осуществлять никаких проверок и он не защитит вас от NRE, в откомпилированном коде вообще не будет разницы, писали вы «MyClass?» или «MyClass» (возможно только метаинформация с аттрибутами).
Если это локальная переменная, то там никто не вклинится. Если это поле или свойство, то такой риск есть, и сейчас компилятор позволяет проверять их, не копируя значение в локальную переменную. Я писал им фидбэк, что для откровенно многозадачного кода хотелось бы иметь более суровые проверки, получил от Мэдса ответ, что это звучит разумно.
Чего действительно не хватает лично мне — это using на «стеке». Хочется избавиться от лишней вложенности, когда у вас есть несколько IDisposable-объектов, которые захватываются в методе, и это полотно из using-ов выглядит некрасиво и многословно. Если бы сделали конструкцию вида
using var recource = CreateDisposable();

Которая бы автоматически открывала using и закрывала его при уходе переменной из области видимости — было бы великолепно. Кажется, когда-то на гитхабе в репозитории Roslyn видел в issues подобный proposal, но с ним что-то было не так. Хотя в C++/CLI такой синтаксис есть и все проблемы решить смогли.
CompositeDisposable уже изобрели
Кажется, когда-то на гитхабе в репозитории Roslyn видел в issues подобный proposal, но с ним что-то было не так. Хотя в C++/CLI такой синтаксис есть и все проблемы решить смогли.

В F# так и сделано, так что C# просто тормозит.


Можно "как на стеке", можно явно скоупы задавать:


Imgur

Я бы хотел видеть наконец возможность прямых вставок IL кода, аналог __asm. И статические шаблоны состоящие из этих команд, которые инлайнятся при компиляции в месте использования. Без этого до сих пор не могу серьезно относится к языку.
У вас есть конкретный пример того, что нельзя описать на обычном сишарпе, но можно на msil? Имхо логичнее расширять язык надежными высокоуровневыми конструкциями, а не давать пользователю лишний способ выстрелить в ногу — для этого уже есть Reflection.Emit.
Найдите номер старшего единичного бита в числе типа long. И замерьте производительность вашего решения. А потом сравните с:

; аргумент — в EAX
bsr ecx, eax

И вы удивитесь насколько хуже ваше решение. А зачем такое делать спросите вы? У меня была практическая задача, делать предварительную оценку сбалансированности ветки двоичного дерева по количеству элементов, и делать это надо было для каждого узла.
Мы про MSIL говорили, а не про ассемблер x86 (который в управляемой среде еще более бесполезен).
Ну вы потрудитесь найти решение моей задачи на C#. А я обещаю что приведу вам еще примеры на IL )).
Если у вас задача по написанию числодробилки с максимальной производительностью, вам управляемые языки как таковые не подходят. Посмотрите в сторону какого-нибудь Rust.
Я ценю ваш юмор, когда у вас в солюшене два десятка проектов C#, как вы будете внедрять туда Rust? Когда вы поддерживаете большие и долгие проекты, иногда так случается, что появляются задачи, на которые вы не рассчитывали… И то что C# не имеет возможности их решать, говорит о том, что это опасный язык, с точки зрения рисков.
Я ценю ваш юмор, когда у вас в солюшене два десятка проектов C#, как вы будете внедрять туда Rust?

Ну вообще есть больше одного решения.


И то что C# не имеет возможности их решать, говорит о том, что это опасный язык, с точки зрения рисков.

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

Не знал, что проектировщик, представляет реализацию каждого метода ). Я вам сразу скажу, у нас полный стек своих решений, от БД до фронта, 1500 сложных запроса в секунду на одно ядро процессора. Это не фантастика. Просто если вы всю жизнь пишите ленивый говнокод вам не понять.

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

Я согласен с тем что инструменты должны использоваться для своих целей. Однако я считаю, что если C++/CLI это не мешает делать вставки в управляемой среде, то почему это должно мешать в C#? Только идеализм и не более того.
Однако я считаю, что если C++/CLI это не мешает делать вставки в управляемой среде, то почему это должно мешать в C#?

Потому что это не является целью C#, а стоимость доработки тулинга высокая.

Ну вообще есть больше одного решения.


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

P/Invoke формата "все вычисление внутри". Да, и данные либо компактные, либо тоже достаются внутри.

Общий принцип известен.

А если в контексте задачи Master_Dante?

Перенести все дерево в неуправляемый код.

И всю логику отвечающую за запросы к этому дереву?

Смотреть уже надо, так сложно сказать.

В целом, меня смущает managed-платформа, из которой везде торчат кишки (иногда самым неожиданным способом) в виде volatile, unsafe, fixed, структур и даже memory padding'а, но к которой я при этом не могу прикрутить хотя бы свой аллокатор и сборщик мусора.

И, да, я видел больше одного проекта на дотнете где это важно и при этом использование дотнета оправдано. И видел задачи, где fine grained il-код может что-то решить.
Выравниваение лечится с помощью атрибута
[StructLayout(LayoutKind.Sequential, Pack = 1)]

Но я полностью с вами согласен. К тому что вы сказали, можно еще добавить это. От C# cкладывается впечатление, что кто то изначально посчитал всех кодеров даунами, у них забрали многие возможности, все ради благих целей и в ущерб производительности. Как то устал от этого, наверно пора менять работу :).
На том уровне на котором дана эта задача ее оптимизировать таким образом невозможно. Потому что .NET (а не язык!) мешает. Нужно смотреть уровнем выше.
Чтобы производительность не умирала на маршалинге — необходимо и достаточно вызывать функции которые выполняют достаточно крупный объем работы, тогда относительные накладные расходы будут маленькими.

Это общий принцип, который одинаково хорошо работает как с P/Invoke, так и с доменами приложений или сетевым взаимодействием.
Ответил выше на комментарий lair.
Если поковыряться наверняка можно обойти маршалинг за счет вызова функций через указатель… Но костыльность будет печалить.
Решений много, серебряной пули нет.

… собственно, почему это вообще проблема языка? Вы какой язык не возьмите, вам CLR не позволит это сделать.

Это вы в MSDN прочитали? Знаете я не пожалел времени ради вашего образования, и сделал проект C++/CLI консольное приложение .Net скомпилировал его и дизасемблировал. Смотрите imgur.com/a/AI6eeRz

Это же банальный P/Invoke (со всеми его накладными расходами). Если вам так надо, то вы прекрасно можете это сделать из C#.

это самый обычный call а не P/Invoke. Если вас сильно смутила эта строчка IL_0004: call int32 modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) foo(int32)
То это договор определяющий порядок следования параметров на стеке вызываемой функции. Если сомневаетесь, то сделайте замер производительности вызова через P/Invoke и напрямую ;).

… а что же там в дизассемблере за .method assembly static pinvokeimpl int32 ... foo(int32 ...) тогда?


Нам пишут:


Managed to Native

In the case where a managed function calls into a native function standard P/Invoke is used. The compiler just defines a P/Invoke signature for the native function in MSIL
Вы оказались правы. Я замерил производительность на 10млн вызовов нативный ~100ms, этой функции 900ms. Потом поковыряюсь, можно ли вызвать функцию по указателю в обход маршалинга.
можно ли вызвать функцию по указателю в обход маршалинга

Можно. Инструкция IL calli.
Хабр
MSDN

Это вы сделали отдельный нативный метод, а не ассемблерную вставку в управляемый.
Ну и что? Когда вам нужно что то перемолотить вы вызываете вот такой метод. Главное здесь это возможности.
Ну так эти возможности уже есть. Всего-то нужно добавить в проект несколько файлов на плюсах и статически слинковаться с ними.
Встроенный метод Integer.numberOfLeadingZeros() (так в Java), который может переводиться JITʼом в bsr или lzcnt для x86 и эффективный аналог на любой другой платформе, это «надёжная высокоуровневая конструкция» или «метод выстрелить себе в ногу»?
Как решение частной задачи оно идеально, но в реальных проектах задачи стоят шире. Реализовать в рантайме методы для ВСЕХ низкоуровневых конструкций — задача космической сложности и трудозатрат, помогающая только проектам узкой ниши. А если реализовать не все, то шаг в сторону — и вы снова упираетесь в производительность и надежнее сразу выбрать язык уровня С.
> Реализовать в рантайме методы для ВСЕХ низкоуровневых конструкций — задача космической сложности и трудозатрат, помогающая только проектам узкой ниши.

Для всех и не надо, но упомянутые вполне относятся к типовым и базовым.

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

При полном отсутствии развития в этой области — подобная позиция переводится как «Чтобы не ошибиться, я просто буду ничего не делать».

(Я знаю как минимум два проекта, где отсутствие названных возможностей привело к тому, что они реализованы на Java, а не C#, это при том, что Windows — одна из целевых платформ. Но, конечно, в общей массе всяких WPF это даже меньше копеек, не спорю.)
Чтобы не ошибиться, я просто буду ничего не делать
Если честно, я не вижу в этом проблемы. Представьте, вы выбираете стек для реализации некоторого приложения и знаете, что вам понадобятся высокопроизводительные вычисления. Выбрать .NET на расплывчатом знании о том, что там «есть что-то базовое», а потом внезапно напороться на отсутствующий функционал и оказаться вынужденным переписывать часть на C — куда более рисковая ситуация для проекта, чем изначально поделить задачу на (условно говоря) интерфейсную и вычислительную части и реализовать их специализированных технологиях.
А если у вас проект которому 10 лет? Предвидеть сторону в которою пойдет проект невозможно, потому что рынок меняется. Поэтому не надо идеализировать. То что вы говорите и так всем известно. Когда у вас будет такой проект, вы рискуете столкнутся с тем, что C# будет не способен решить какой то очень узкий момент. Следовательно, стоит ли вообще на нем писать новые проекты, вот главный вопрос.

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

В данном случае у меня получалось и не один раз, что нет «рисков использования managed языков», а есть риск использования конкретного C# и нет такого риска для Java.
И это удручает, учитывая, что C# как язык таки заметно прогрессивнее, и частично это так для инфраструктуры разработки.
Уточню сразу специфику: разная сетевая активность системы «специализированные демоны/серверы/прокси/etc.», практически всегда под unix-like.
bsr ecx, eax

А как вы это собираетесь сделать на IL?

Хахаха налетели )), следовательно надо сделать и ассемблерные вставки, и не надо меня посылать в C++, язык должен делать меня свободным от узости применения.
следовательно надо сделать и ассемблерные вставки

Ассемблерные вставки? В управляемой среде? Просто нет.


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

Язык вам ничего не должен.

Тем не менее решение некоторых задач, гораздо эффективнее находясь ближе к реальному железу, а не к идеалам. И это факт.

Для этих задач использование .net в целом неэффективно.

Тем не менее решение некоторых задач, гораздо эффективнее находясь ближе к реальному железу, а не к идеалам. И это факт.

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


А для тех редких случаев когда нужна прям мега оптимизация как на асме — C/C++

Спасибо друзья вы как всегда предлагаете свежие решения ))
> Ассемблерные вставки? В управляемой среде? Просто нет.

Почему в C++/CLI можно свободно управляться с указателями, с шансом разломать к чертям управляемую кучу, а если ассемблерные вставки так сразу нет? По последствиям это одно и то же.
Все просто. Работу с указателями довольно просто реализовать и даже можно оптимизировать. А ассемблерные вставки нормально сделать все равно не получится.
В GCC получилось нормально сделать ассемблерные вставки. Кто хочет, ищет метод, кто не хочет — ищет отмазку.
(Специфика управляемой среды тут заметна, да, но не фатальна.)
Если речь о принципиальной возможности, то точно так же.
Если о текущей практике, то дорогой FFI и отсутствие переходных средств в стиле C++/CLI заметно мешают нам прямо сейчас.
Ага. С костылями в виде fixed и stackalloc и невозможностью явно что-то выделить/удалить в куче.

Здрасьте! А что по-вашему Marshal.AllocHGlobal и Marshal.FreeHGlobal делают если не явно выделяют/удаляют в неуправляемой куче? И тот IntPtr который возвращает AllocHGlobal можно преобразовывать к указателю через ToPointer() и использовать без всяких fixed.


Что же до stackalloc — это не костыль, а аналог функции alloca.

Виноват, неправильно сформулировал мысль.

Как я писал выше — я хочу СВОЙ аллокатор. Например, чтобы гарантировать что созданные объекты легли в одну страницу памяти. Или обеспечить хорошую data locality без танцев с массивами и структурами.

Я бы предпочел, чтобы и указателей не было.

Не, если их использовать очень трудно — то пусть будут.

Мне не жалко.

Ну когда вокруг >90% процессоров будут нести в себе сразу managed-среду — почему бы и нет.
Пока что я вижу, что попытки такое сделать откатились назад — даже AS/400 превратилась просто в специфический враппер вокруг Power.
Вот тут надо таки сравнивать с Java.
Нужен bsr? Integer, Long знают numberOf{Leading,Trailing}Zeros(), bitCount() и прочие аналогичные вкусности, которые переводятся в одну команду. Безо всяких ассемблерных вставок.
Или вот: BigInteger там же знает про bitLength(), который реализовать тривиально и дёшево. Аналог C# не знает такого, зато зачем-то дал IsPowerOfTwo, который дорог и тупо бессмысленен. И таких моментов много — непонятно, то ли намеренно зарезали возможности, которые облегчают работу на .NET, чтобы гнать людей на C++/CLI или чистый C++, то ли у авторов этого в принципе потеряна обратная связь с пользователями.
Вообще, такая задача вполне себе стандартно решается с помощью lookup tables, которые на C# будут работать примерно так же как и в нативном коде.
Не буду оригинальным, C# скатывается в *оно. Былых профи уж нет, а новые — хипстота зелёная, прыгают по фичам, как блондинка в обувном. О крупных фичах нужно было заботиться с самого начала (благо, ВСЁ, что MS вытуживает только сейчас, известно уже лет 20). А когда берут посредственную Жабу, обзывают её C# (вот дебил постарался!!), а потом изображают «развитие языка» — это не работает. Всхлипперт — он может и умный, но у него не хватает совести признать, что язык был непроработан и КАЖДОЕ его улучшение идёт либо половинчатыми мерами, либо каким-то анальным путём.
Будущего у C# нет, его похабят с каждой версией команда индусни, нанятая непонятно зачем.
По-моему, сейчас самое время заняться Nemerle-2 (не Нитрой!) — грамотно спроектированный язык просто размажет всю эту C# team со всеми их поделками и сахарочками.
А зачем сделали Nemerle, если уже был F#?
Метапрограммирование, макросы.
Nemerle вроде как раньше появился чем в F#.
В 2003 году аж, а F# в 2005.
Ну и про метапрограммирование уже сказали.
Всхлипперт — он может и умный, но у него не хватает совести признать, что язык был непроработан

Если что, главный по C# вот этот парень


По-моему, сейчас самое время заняться Nemerle-2

Какую проблему решит Nemerle-2 по сравнению с C#, F# (и чо уж там, Nemerle-1)?

Шарпом занимается Mads Torgersen

Это прожект манагер же, а не архитектор.

По-моему, Hejlsberg активно C# сейчас не занимается. Видео в этом плане показательное.

Хайлзберг — архитектор, а про Всхлипперта я в контексте его бесконечных оправданий «почему НЕТ, почему НЕ ТАК, почему ПОЗДНО» и т.п. И всегда ведь выкручивается, Штирлиц! :) Вообще, довольно странно иметь такую похабщину как C# 1.0, имея за плечами индустрии Perl, Lisp, Smalltalk и прочие языки. :(

Какую проблему решит Nemerle-2


Немерля (для начала) решает одну главную проблему — вот эти узколобые Хайлсберги во главе. Что бы ты ни делал, куда б не писал, всё сходится к кучке так называемых «разработчиков языка», через кость которых к мозгу пробиться невозможно. Немерля — открытый проект: хочешь — форкай, хочешь — пиши патчи, И ЭТО КОМПИЛИРУЕТСЯ! Причём вплоть до .NET 2.0; Казалось бы, с какого перепоя вдруг Roslyn'у понадобились все эти .NET 4.6, Core, PowerShell… да всё просто — язык внаглую похабят так, чтобы кроме как в вендюшной десяточке он нигде не канпелялся! Вот не скоты?..

А далее без остановок: свой синтаксис, свои идеи, DSLи всякие… Влад много об этом рассказывал. Как-то глупо даже спрашивать, «что решит Немерле», зная (если зная!) о его возможностях — это язык на порядок выше любой сегодняшней сишарпщины. А Немерле-2 решит проблему «не очень проработанной» базы Немерли-1, только и всего — сам язык менять почти не надо.
Кто-нибудь знает, планируется ли добавлять поддержку Records в Entity Framework, хотя бы в запросы (или, может быть, она уже есть в Core)?

Шестая версия EF такого точно не «прожует»:

db.Swords
    .Select(s => new Sword(s.Damage, s.Durability))
    .OrderBy(s => s.Damage).First(); // ошибка: неизвестное свойство "Damage"

db.Swords
    .Select(s => new Sword { Damage = s.Damage, Durability = s.Durability))
    .OrderBy(s => s.Damage).First(); // а так нормально
И заодно сделали бы возможность Select напрямую в кортеж, без создания промежуточного анонимного класса.
А во что эти nullable компилируются? Если я подключу готовую сборку, в ней же nullable будут сохранены?
Это было бы очень плохим решением. Лучше что-нибудь аналогичное Nullable для значений, типа NullableRef<>.
Нет, это единственное решение которое совместимо с другими языками и legacy кодом.
Это вообще не решение, т.к. должно соблюдаться typeof(T?) != typeof(T). Вводится, по сути, новый обобщённый тип данных, поэтому любая его реализация не нарушит обратной совместимости, т.к. раньше его просто не было.
должно соблюдаться typeof(T?) != typeof(T)

Угу, а еще должно соблюдаться typeof(T?).IsAssignableFrom(typeof(T)), правда же?

Это ведь решено для value-типов?

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

Так или иначе, атрибуты ничего не решают.

Атрибуты как раз решают: они позволяют сделать эту фичу на уровне языка, а не фреймворка.

Не вижу причин не сделать по аналогии с value-типами.

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

Атрибуты как раз решают: они позволяют сделать эту фичу на уровне языка, а не фреймворка.

Атрибуты — это же не языковая конструкция. Это добавляет управляющего кода для исполнения. Обслуживается средой исполнения, ну, или можно кодогнерацией управлять. Так что получается на уровне фреймворка.
Это добавляет управляющего кода для исполнения.

Нет, это добавляет метаданных для анализа.


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

Насколько я помню текущую реализацию, CLR никак не использует расставляемые C# 8 атрибуты, они используются только при анализе времени компиляции.

Что насчет, например Declarative Security:
CodeAccessSecurityAttribute
DnsPermissionAttribute
SocketPermissionAttribute

или
ThreadStaticAttribute

и еще куча разных интересных атрибутов?

Кто как не CLR их использует?
А их что, C# 8 тоже теперь расставляет?

Я же написал "расставляемые C# 8 атрибуты". А в контексте разговора речь и вовсе идет о конкретном System.Runtime.CompilerServices.NullableAttribute.

Так там всего один атрибут, а не два? Вот это они зря :-(

Я вижу только один.

А можете подсказать ссылку на документацию MS по этому атрибуту?

Нет, я смотрю на сгенеренный код на шарплабе.

Попробовал на шарплабе вот такой код:
using System;
public class C {
    class Foo{
    }
    struct Bar{
    }
    public static void Main() {
        Console.WriteLine(typeof(Foo) == typeof(Foo?) );
        Console.WriteLine(typeof(Bar) == typeof(Bar?) );
    }
}

Получаем:
True
False

По-моему, это ужасно. Да, там действительно атрибут. Надеюсь, в релизе такого поведения не будет.
Вы, вероятно, не узнали синтаксис, использованный в последнем методе класса

Те кто пишет на Kotlin легко поняли всю декларацию )
MemoryMarshal.Cast костыль для идеологических ненавистников указателей. Это похоже на совок, жопа есть а слова нет ). Надо покидать всю эту вакханалию и идти в С++
Sign up to leave a comment.

Articles