Кое-что о неявной типизации

Отгремели холивары. Герои починяют копья и готовятся к будущим битвам. Ну, а я выскажусь.
На мой взгляд, Илья выступил в пользу повсеместного использования неявной типизации в C# очень мощно и сурово, но абсолютно неубедительно.
Во всяком случае, с моей точки зрения.
Посудите сами→

Коллекция граблей

Наткнувшись сегодня в статье Александра Галкина «Немного об интерфейсах в .Net (по мотивам одного интервью)» на тот факт, что массив в .NET, оказывается, реализует многие члены интерфейса IList выбрасыванием NotSupportedException, я крайне удивился. Удивился, потому что привык, что .NET страхует программиста, не давая ему наступать на грабли, но в данном случае грабли лежат в основе основ фреймворка, в наисистемнейшем классе System.Array.  В чём же грабли, если эта недореализация недоступна в публичной области видимости? В возможности неявного приведения без каких-нибудь предупреждений.

Объявляет, к примеру, какой-нибудь программист интерфейс:

interface I
{
    void Test(IList collection);
}

Имеет, в общем-то, право.

Далее, какой-нибудь другой программист (ну, или тот же, неважно) реализует этот интерфейс:

class C : I
{
    public void Test(IList collection)
    {
        collection.Clear();
        // разные другие манипуляции
    }
}

И тоже имеет на это полное право.

Но потом приходит третий программист (ну, или тот же самый, но спустя пару месяцев), и пытается использовать этот интерфейс:

    C c = new C();
    c.Test(new[] { 1, 2, 3 });

И… получает NotSupportedException без объявления войны.
Особенно грустно становится, если вспомнить о том, что некоторые разработчики питают нездоровое пристрастие к неявной типизации (впрочем, про неё лучше отдельно как-нибудь), что чревато подобными участками кода:

    var integers = SomeFarObject.GetSomeCollectionOfIntegers(); // int[] GetSomeCollectionOfIntegers()…
    c.Test(integers);

Ой. А стукнет по лбу уже в рантайме — эксепшен вылетит прямо в лицо тестировщику (хорошо, если не клиенту). Неприятно.
Кто-нибудь в курсе, зачем System.Array реализует IList, с какой целью?

Использование атрибутов безопасности для контроля доступа к коду

Довелось мне недавно перерабатывать модель безопасности на немаленьком проекте с трудной судьбой. Модель получилась удобная, изящная, практичная, в ней по уму реализованы и красиво вписаны в бизнес-логику всякие участники, удостоверения и роли, и единственное, чего не хватало до полного счастья — атрибутов для лаконичного контроля использования кода вида «этот метод запускается только если у текущего пользователя есть такая-то роль». У меня внезапно нашлось несколько часов времени на исследования в этой области, и при ближайшем рассмотрении не менее внезапно оказалось, что для моей ситуации в .NET уже всё есть, осталось только научиться этим пользоваться. И тут внезапно оказалось (снова!), что готовый и полный howto на эту тему нагуглить неожиданно сложно — по крайней мере, мне не удалось этого сделать. Целостную картину пришлось собирать по кусочкам, и вот что получилось…

Такие разные одиночки

Данная запись является вольным переводом статьи Джона Скита «Implementing the Singleton Pattern in C#» (англ.), которая чрезвычайно понравилась мне как полнотой материала, так и манерой его подачи (с объяснением причин, особенностей и последствий).

Введение

Паттерн одиночка — один из наиболее известных паттернов проектирования. По сути, одиночка — класс, позволяющий создать лишь один свой экземпляр и обычно предоставляющий простой способ доступа к этому экземпляру. Чаще всего одиночки при создании экземпляра не позволяют указывать никаких параметров, так как в этом случае удовлетворение второго обращения с отличающимися параметрами было бы проблематично (в случае, если конкретный экземпляр должен быть доступен вызовам с одинаковыми параметрами, лучше использовать фабрику). Эта статья относится только к ситуации, когда параметры не нужны. Обычным требованием к одиночкам является ленивая инициализация, то есть создание экземпляра только тогда, когда в нём впервые возникает необходимость.
Реализовать одиночку в C# можно несколькими различными способами. Я опишу их здесь в порядке возрастания изящества, начиная с наиболее часто встречающегося потоконебезопасного способа и заканчивая самой ленивой, простой, потокобезопасной и производительной реализацией. Читать далее

Используя using

Один из моих самых любимых вопросов на собеседовании: в чём опасность такой конструкции?

…
using (MyClass myClass = new MyClass() { Prop = p })
{
    myClass.Use();
}

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

…тем больше я люблю C#

Нашел недавно забавный трюк.
Иногда при реализации интерфейса удобно какой-либо из объявленных членов реализовать производным типом. Как-то так.

private interface I
{
    object P { get; }
}

private class C : I
{
    public string P
    {
        get
        {
            return "Hello world!";
        }
    }
}

Но напрямую, в лоб, сделать этого нельзя: сигнатуры членов класса и интерфейса получаются разными, и интерфейс очень обижается, не признавая такую реализацию, несмотря на то, что string прекрасно приводится к object.

Program.cs(12,17): error CS0738: 'Test1.Program.C' does not implement interface member 'Test1.Program.I.P'. 'Test1.Program.C.P' cannot implement 'Test1.Program.I.P' because it does not have the matching return type of 'object'.
Program.cs(9,11): (Related location)
Program.cs(14,18): (Related location)

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

private interface I
{
    object P { get; }
}

private class C : I
{
    public string P
    {
        get
        {
            return "Hello world!";
        }
    }

    object I.P
    {
        get
        {
            return this.P;
        }
    }
}

Правда, я совершенно не понимаю, откуда взялось ограничение, не позволяющее производному классу замещать базовый в этом контексте; более того, я без понятия, какими словами это гуглить. В msdn я ничего не нашел, лишь только аксиоматическое: жизнь несправедлива …член класса… …должен обладать таким же именем и сигнатурой, как член интерфейса, смирись с этим. Более того, я не совсем уверен, что у этого поведения есть цель, а не причина. В любом случае, если кто-нибудь знает причину, цель или кого-нибудь, кому уместно задать такой вопрос — черкните, пожалуйста, пару слов в комментариях.

О литералах, регулярных выражениях и пользе документации

Один из моих коллег недавно наступил на грабли, словив глюк с неочевидными на первый взгляд симптомами, которые и сформулировать-то затруднительно. В общем, дело было так: сидит он себе, никого не трогает, программу пишет. Конкретно — фильтр, пропускающий лишь символы из определенного диапазона. Достаточно широкого, кстати: по сути, требовались отсечь управляющие символы, за исключением некоторых (отдельно хочу отметить сам подход: отсечь всё, кроме нужного, а не отсечь ненужное — это правильно; почему-то разница очевидна не для всех). Читать далее