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

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

Posts Tagged ‘collections

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

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 at 23:48

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

Tagged with