Дмитpий Hecтepук

Блог о программировании — C#, F#, C++, архитектура, и многое другое

Немутабельные коллекции

5 комментариев

Если переключить NuGet в пререлизный режим, один из пакетов который вы найдете называется Microsoft Immutable Collections. Немутабельность магически приходит в .NET в основном под влиянием F#: тут, относительная неизменяемость встроена в язык, хотя по-хорошему, что касается коллекций, у F# те же проблемы что и у C# т.к. массивы мутабельны: x.[0] = 123; вполне валидное выражение, и let mutable для этого писать необязательно.

Что такое немутабельная коллекция?

Если коротко, то

  • Немутабельную коллекцию нельзя менять

  • Но методы изменения все-таки есть!

  • И каждый метод создает и возвращает новый инстанс коллекции

Естественный вопрос зачем? Классическая реплика насчет потокобезопасности как бы не работает, т.к. есть System.Collections.Concurrent или как там. Но с другой стороны, существуют вполне реалистичные ситуации, в которых нужно и обновить коллекцию и тем не менее оставить предыдущий вариант. Хотите пример? Пожалуйста!

Надуманный пример

Есть такая штука – брокер событий. Это фактически специализированный service bus для приложения, с тем намеком что все события системы кладутся на некую шину через Publish() и потом снимаются с нее с помощью Subscribe(). Соответственно класть в него событие – кошерно, а вот комманду (привет CQRS) – это уже на любителя.

Так вот, у брокера событий есть набор подписок:

private readonly List<Subscription> subscribers = new List<Subscription>();

Есть метод для подписок

public IDisposable Subscribe(IObserver<EventArgs> subscriber)
{
  Subscription sub = new Subscription(this, subscriber);
  if (!subscribers.ToArray().Any(s => s.Subscriber == subscriber))
  {
    subscribers.Add(sub);
  }
  return sub;
}

и для рассылки событий:

public void Publish<T>(T args) where T : EventArgs
{
  foreach (var s in subscribers.ToArray())
    s.Subscriber.OnNext(args);
}

Заметили использование ToArray()? С чего бы это все материализовывать? А с того что подписки и отписки фактически могут меняться в процессе, и материализация — единственный процесс, который спасает нас от этого. Другая альтернатива — как раз немутабельные коллекции. Cам список можно опеределить вот так:

private ImmutableHashSet<Subscription> subscriptions = ImmutableHashSet.Create<Subscription>();

Уже нет readonly т.к. вся суть этой переменной в том, чтобы ее перезаписывать — напомню что .NET гарантирует атомарность этой операции. Соответственно, метод подписки выглядит вот так:

public IDisposable Subscribe(IObserver<EventArgs> subscriber)
{
  var s = new Subscription(this, subscriber);
  subscriptions = subscriptions.Add(s);
}

А соответственно метод рассылки вот так:

public void Publish<T>(T args) where T : EventArgs
{
  foreach (var s in subscriptions)
    s.Subscriber.OnNext(args);
}

Это тоже валидно. Напомню что переменная считывается только первый раз, а далее, даже если ее перезаписали, это не важно — ведь это уже совсем другая переменная.

Что есть в коробке?

Доступен полный набор коллекций – array, list, stack, dictionary, sorted dictionary, queue, hashset, sorted set.

Также доступен ImmutableInterlocked, содержит методы для атомарного присваивания, замены, вообщем все как Interlocked для немутабельных коллекций. В принципе, в примере выше я должен был пометить поле как volatile и воспользоваться этим классом для полного счастья.

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

Реклама

Written by Dmitri

20 июля 2013 в 23:48

Опубликовано в .NET

Tagged with

комментариев 5

Subscribe to comments with RSS.

  1. Если речь идет о многопоточном приложении, то не понятно, как может работать оригинальный метод Subscribe в многопоточной среде

    Во-первых, он не гарантирует, что в коллекции не будет дубликатов – 2 разных потока могут одновременно вызывать .ToArray(), убедиться, что в массиве нет нужного экземпляра и затем добавить его в исходный List 2 раза.

    Во-вторых, может выдавать непредсказуемые исключения, так как класс List не является потокобезопасным, и, например, если оба потока одновременно вызовут .Add, то List может просто выбросить неопределенное исключение и перейти в некорректное состояние.

    Artem Kryvokrysenko

    21 июля 2013 at 0:17

    • Да, это так. Я вырыл из закромов нечто что вероятно задумывалось потокобезопасным (поэтому там ToArray()) но потом на это было забито ввиду того, что подписки обычно инициализируются в конструкторах компонент.

      Dmitri

      22 июля 2013 at 12:08

  2. Дмитрий, несколько замечаний:
    1. Непонятно назначение статьи. Вы хотели рассказать о неизменяемых коллекциях? Получилось не очень, т.к. изложение скомканое и базируется на единственном примере, где вообще не объяснено, «чем стало жить лучше».
    2. Постарайтесь использовать русскую терминологию, вот это «иммутабельное» режет слух и напоминает жаргон «Успешных Менеджеров».
    3. «нужно и обновить коллекцию и тем не менее оставить предыдущий вариант» — извините, это ВСЁ, для чего нужны неизменяемые коллекции??
    4. Immutability по-идее вообще не должна заботить программиста — это забота компилятора. Кроме того, не для программиста это должен быть бенефит, а для ДотНетины — она ВОЗМОЖНО получит какой-то выйгрыш от того, что ссылку нельзя перезаписать (откровенно, я в это не особо верю).

    Так зачем нужны неизменяемые коллекции?…

    Mememe

    21 июля 2013 at 11:03

  3. Соглашусь с предыдущим комментатором. Извиняюсь за критику, но:
    1. Немутабельные — неизменяемые либо immutable. Остальное еще не прижилось и режит слух.
    2. Тема немаленькая, статья коротковата, и возникает только больше вопросов.

    Кому ново и возник интерес:
    1. http://blogs.msdn.com/b/andrewarnottms/archive/2011/08/22/read-only-frozen-and-immutable-types-and-collections.aspx
    2. http://blogs.msdn.com/b/bclteam/archive/2012/12/18/preview-of-immutable-collections-released-on-nuget.aspx

    vtl

    21 июля 2013 at 12:33

  4. Для чего нужны Immutable Collections
    1) Функционального программирование на C#, теперь можно писать «чистые» функции которые принимают на вход список
    2) Снэпшоты, пример красно зеленые деревья Roslin http://blogs.msdn.com/b/ericlippert/archive/2012/06/08/persistence-facades-and-roslyn-s-red-green-trees.aspx

    Наиль (@NAchmedzhanov)

    22 июля 2013 at 15:10


Оставить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

%d такие блоггеры, как: