Ну а если отсмеялись — посмотрите ещё на Scala-обёртку вокруг вышеупомянутой Trireme, настоящей node.js и пары ещё каких-то вариантов: github.com/typesafehub/js-engine
А если бы C++ разрабатывался с нуля. Например, если бы в нем не было чистого C, были только «умные» указатели, да и к тем же целым числам нельзя было применять if («int i = 0; if (i =1) { }»), а только к нативному bool, и десятки подобных моментов?
Мне кажется, вы сейчас описываете некий такой аналог Rust, D или Go :)
В принципе — да, относится, но, честно признаться, при написании статьи я не знал / забыл про эту фичу с непривязанными делегатами, поэтому написал максимально обтекаемо :)
Мало того, статический конструктор и статический инициализатор — это разные штуки, и порядок их вызова тоже как-то хитро определяется (пардон, вы про это и написали, сходу неверно проинтерпретировал ваш комментарий). В старых версиях рантайма добавление статического конструктора меняло логику всей инициализации, вот Скит про это пишет подробно.
Возможно, виноват был порядок инициализации статических полей или даже порядок вызова статических инициализаторов разных классов? Сам по себе этот участок не выглядит ошибочным, по-моему.
И ещё — к делу не относится, но мне кажется, что тут хорошо бы спецификатор readonly добавить.
Проверка туда поставлена для того, чтобы null.Equals(null) не возвращал false, как было в .NET 2.0, а кидал исключение.
Да, тут вы правы, я сейчас исправлю этот фрагмент в статье.
Небезопасный код всё равно кинет NullReferenceException.
С небезопасным кодом не всегда можно быть уверенным во всём. Вот есть, например, такой пример из книжки «Expert .NET 2.0 IL Assembler» (это вызов sscanf с null в качестве форматной строки). Автор утверждает, что у него этот код падал с NullReferenceException, однако в современном рантайме он кидает другое исключение — AccessViolationException. Я, конечно, понимаю, что интероп и unsafe — это разные вещи, но лучше, на мой взгляд, таким не баловаться и нуллы не передавать ни туда, ни туда.
Во-первых, код всё равно упадёт на первой попытке доступа к такому объекту.
Да, конечно, упадёт. Но стоит учитывать, что в этом случае конечный пользователь может увидеть невнятный стектрейс. Я считаю, что если вы пишете библиотеку, которую будут использовать сторонние пользователи — лучше по возможности проводить валидацию аргументов на входе в библиотеку, и выбрасывать такого рода исключения сразу при вызове публичного API, а не откуда-то изнутри своего кода.
Во-вторых, это не спасёт от 1 вместо 0 в this.
А есть ли какой-то валидный и не слишком сложный способ передать единицу в качестве managed-ссылки?
По поводу проверки типов f(string a) { if (!(a is string)) — я верю, что каким-то волшебным макаром можно вызвать такую функцию, подсунув ей не строку. Но как именно? Вроде как sidristij что-то такое делает, но в нормальном коде такое встречается? Я же тут вам не сказки рассказываю, а описываю самый настоящий баг, с которым я реально столкнулся. В любом случае, код, который вытворяет такое, явно делает это злонамеренно, и бороться с ним не особо-то продуктивно, как вы верно подметили.
Нет, никак не транслируются, friend это фича исключительно на уровне нативного исходного кода C++. Так что от статических методов нам пока никуда не уйти.
Хм, интересно, а friend-нотация из C++ как-нибудь транслируется в CLI? Если да, то она бы могла помочь получить доступ к приватным полям, и дело «за малым» — получить соответствующий синтаксис в C# :)
Я только припоминаю, что в VB.NET есть какие-то Friend Class'ы, но, кажется, это что-то типа нашего internal.
Более подробно (с примерами кода, если кому непонятно пояснение) об этом когда-то писал Липперт. Примеры, правда, на VB.NET, потому что в 2004 году только он поддерживал фильтры исключений.
Видны большие синтаксические изменения TypeScript 1.5 в плане модулей. Это позволит более тонкую интеграцию с ангуляровскими компонентами? Замечательно! А где можно почитать про изменения в 1.5?
Только что проверил вот на такой простой программе:
class Program
{
static int Recursive(int x)
{
if (x == int.MaxValue)
{
throw new Exception();
}
return Recursive(x + 1);
}
static void Main(string[] args)
{
int x = Recursive(0);
Console.WriteLine(x);
}
}
В AnyCPU под 64-битной осью оптимизация не срабатывает, в дебаге — тоже. Если компилять под x64-only и в релиз — тогда начинает работать. Стек действительно портится — показывает, что упал в Main -> Recursive без отслеживания хвостовых вызовов. Ну что ж, нормально — лучше уж так, чем вообще без оптимизации.
Вообще говоря, мне не кажется, что TCO такая уж важная вещь в .NET именно по причине того, что она не гарантируется. Вот если она будет гарантирована стандартом в каких-то случаях, или же можно будет развесить какие-то атрибуты, которые бы проверяли её наличие и падали, если оптимизировать не получилось (типа @tailrec в Scala) — это было бы нормально. Моя мотивация проста — TCO это крайне важная особенность поведения кода, которая может сильно влиять на его быстродействие и характеристики потребления памяти, ну а писать рабочий код, который полагается на какие-то негарантированные оптимизации компилятора и ломается, если у компилятора или JIT'тера что-то там «не получилось» — это уж слишком, по-моему.
Ну а если отсмеялись — посмотрите ещё на Scala-обёртку вокруг вышеупомянутой Trireme, настоящей node.js и пары ещё каких-то вариантов: github.com/typesafehub/js-engine
И ещё — к делу не относится, но мне кажется, что тут хорошо бы спецификатор
readonly
добавить.Да, тут вы правы, я сейчас исправлю этот фрагмент в статье.
С небезопасным кодом не всегда можно быть уверенным во всём. Вот есть, например, такой пример из книжки «Expert .NET 2.0 IL Assembler» (это вызов
sscanf
сnull
в качестве форматной строки). Автор утверждает, что у него этот код падал сNullReferenceException
, однако в современном рантайме он кидает другое исключение —AccessViolationException
. Я, конечно, понимаю, что интероп иunsafe
— это разные вещи, но лучше, на мой взгляд, таким не баловаться и нуллы не передавать ни туда, ни туда.Да, конечно, упадёт. Но стоит учитывать, что в этом случае конечный пользователь может увидеть невнятный стектрейс. Я считаю, что если вы пишете библиотеку, которую будут использовать сторонние пользователи — лучше по возможности проводить валидацию аргументов на входе в библиотеку, и выбрасывать такого рода исключения сразу при вызове публичного API, а не откуда-то изнутри своего кода.
А есть ли какой-то валидный и не слишком сложный способ передать единицу в качестве managed-ссылки?
По поводу проверки типов
f(string a) { if (!(a is string))
— я верю, что каким-то волшебным макаром можно вызвать такую функцию, подсунув ей не строку. Но как именно? Вроде как sidristij что-то такое делает, но в нормальном коде такое встречается? Я же тут вам не сказки рассказываю, а описываю самый настоящий баг, с которым я реально столкнулся. В любом случае, код, который вытворяет такое, явно делает это злонамеренно, и бороться с ним не особо-то продуктивно, как вы верно подметили.friend
это фича исключительно на уровне нативного исходного кода C++. Так что от статических методов нам пока никуда не уйти.Я только припоминаю, что в VB.NET есть какие-то Friend Class'ы, но, кажется, это что-то типа нашего
internal
.В AnyCPU под 64-битной осью оптимизация не срабатывает, в дебаге — тоже. Если компилять под x64-only и в релиз — тогда начинает работать. Стек действительно портится — показывает, что упал в
Main -> Recursive
без отслеживания хвостовых вызовов. Ну что ж, нормально — лучше уж так, чем вообще без оптимизации.Вообще говоря, мне не кажется, что TCO такая уж важная вещь в .NET именно по причине того, что она не гарантируется. Вот если она будет гарантирована стандартом в каких-то случаях, или же можно будет развесить какие-то атрибуты, которые бы проверяли её наличие и падали, если оптимизировать не получилось (типа
@tailrec
в Scala) — это было бы нормально. Моя мотивация проста — TCO это крайне важная особенность поведения кода, которая может сильно влиять на его быстродействие и характеристики потребления памяти, ну а писать рабочий код, который полагается на какие-то негарантированные оптимизации компилятора и ломается, если у компилятора или JIT'тера что-то там «не получилось» — это уж слишком, по-моему.