LoginSignup
26
24

More than 5 years have passed since last update.

Immutableなモノたち

Last updated at Posted at 2016-07-26

どーいうことだってばよ?

2016/07/26は雨が降って、自転車に乗れなかったのでむしゃくしゃしてやった。後悔はしていない。
System.Collections.Immutableを簡単にまとめていこうかなって。

どこから取れるの?

System.Collections.Immutable
このあたりから落としてくれば良いかと。ただ、VisualStudio使ってるなら、Nugetでさくっと落としてきた方が楽です、えぇ。

Mutableなモノたち

普段、一般的に使っているList<T>や、配列はImmutableでは無いので、以下のような操作をした結果、操作したインスタンスの状態が変化する。


using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var list = new List<int> {0, 1, 2, 3, 4, 5};

            list.Add(42);
            list.RemoveAt(0);
            list[0] = 114514;


            foreach (var i in list)
            {
                Console.WriteLine(i);
            }
            //114514
            //2
            //3
            //4
            //5
            //42
            //が表示される。
        }
    }
}

このように、AddRemoveAt、インデクサで値を追加・削除・変更すれば当然、listの内容は変更される。この辺は、いつも通りなので、流す程度で。

Immutableとは

じゃあ、Immutableってばなによ?ってなると、文字通り不変であると言うことになりまして、まぁ、その辺は見て頂いた方が早いかなってコトデ、以下サンプル


using System;
using System.Collections.Immutable;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            //ImmutableList<T>はCtorを持ってないので、ファクトリ使って構築する。
            var immutableList = ImmutableList.Create(0, 1, 2, 3, 4);

            //ふつーにforeachで回せる。
            foreach (var i in immutableList)
            {
                Console.WriteLine(i);
            }
        }

    }
}

このように、構築方法は若干違うけど、普通のList<T>とかとまぁだいたい同じようなことが出来る。

Listだし、要素の追加と削除はしたいよね

まぁ、のっけから矛盾するようなこと言ってますが、実際可能です。ただ、『不変である』と言う部分と喧嘩しないような手を考えないとまずいわけですが。コレも実際サンプル見て頂ければ


using System;
using System.Collections.Immutable;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            //ImmutableList<T>はCtorを持ってないので、ファクトリ使って構築する。
            var immutableList = ImmutableList.Create(0, 1, 2, 3, 4);

            Console.WriteLine("Before the operation.");
            //ふつーにforeachで回せる。
            foreach (var i in immutableList)
            {
                Console.WriteLine(i);
            }

            //Addできる
            immutableList.Add(42);

            //RemoveAtも出来る
            immutableList.RemoveAt(0);

            Console.WriteLine("After the operation.");

            //けれど不変(ナンデ?)
            foreach (var i in immutableList)
            {
                Console.WriteLine(i);
            }
        }
    }
}

けど、結果は以下の通りになって、多分想定通りに動かない。

efore the operation.
0
1
2
3
4
After the operation.
0
1
2
3
4

まぁ、不変なのだし、変わっちゃ困る反面、じゃ、なんで、Addやら、RemoveAtみたいな操作系のメソッドが有るのかと言うことになる。

じゃあ、操作して変化した後の状態はどこから取得できるかというと、戻り値として取得可能となっている。以下サンプル


using System;
using System.Collections.Immutable;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            //ImmutableList<T>はCtorを持ってないので、ファクトリ使って構築する。
            var immutableList = ImmutableList.Create(0, 1, 2, 3, 4);


            //Addできる
            var addAfterList = immutableList.Add(42);

            //RemoveAtも出来る
            var removeAfterList = immutableList.RemoveAt(0);


            //元は不変
            Console.WriteLine("immutableList");
            foreach (var i in immutableList)
            {
                Console.WriteLine(i);
            }


            //Addはされている
            Console.WriteLine("addAfterList");
            foreach (var i in addAfterList)
            {
                Console.WriteLine(i);
            }

            //RemoveAtもされている
            Console.WriteLine("removeAfterList");
            foreach (var i in removeAfterList)
            {
                Console.WriteLine(i);
            }
        }
    }
}

immutableList
0
1
2
3
4
addAfterList
0
1
2
3
4
42
removeAfterList
1
2
3
4

このように、操作した結果は戻り値として取得可能で有り、元の構造には不変なまま。なので、不変を担保しつつ操作することを許容していることになる。

これが、Immutalbeなモノたちの基本的な考え方になる。

有りモノの変更

じゃあ、既存の値をインデクサ経由で変更可能かというと、残念ながら不可能。但し方法はある。SetItemを使うことで、操作ができる。


using System;
using System.Collections.Immutable;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            //ImmutableList<T>はCtorを持ってないので、ファクトリ使って構築する。
            var immutableList = ImmutableList.Create(0, 1, 2, 3, 4);

            var after = immutableList.SetItem(0, 42).SetItem(1, 114514);

            //当然不変
            Console.WriteLine("immutableList");
            foreach (var i in immutableList)
            {
                Console.WriteLine(i);
            }

            //変更されている
            Console.WriteLine("after");
            foreach (var i in after)
            {
                Console.WriteLine(i);
            }
        }
    }
}

immutableList
0
1
2
3
4
after
42
114514
2
3
4

Builderを使う別解

ToBuilderメソッドでビルダーこさえて、そいつを操作することでも諸々の操作は可能。元のImmutableなモノをMutableにして、操作して、またToImmutableでImmutableにするので、イメージとして、System.StringSystem.Text.StringBuilderみたいな関係になるかなって。当然、ToBuilderでこさえた、ImmutableList<T>.BuilderオブジェクトはMutableなので、その辺はご注意の程。以下はその辺のサンプル。


using System;
using System.Collections.Immutable;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            //こさえる
            var immutalbe = Enumerable.Range(0, 10).ToImmutableList();

            //Builderをこさえる。
            var build = immutalbe.ToBuilder();

            //Builderに対してはMutableな操作が効く。
            build[0] = 42;

            //当然操作の結果は変化する。
            Console.WriteLine("build");
            foreach (var i in build)
            {
                Console.WriteLine(i);
            }

            //ToImmutableでImmutable化できる。
            var after = build.ToImmutable();

            //操作結果は反映される
            Console.WriteLine("after");
            foreach (var i in after)
            {
                Console.WriteLine(i);
            }
        }
    }
}

build
42
1
2
3
4
5
6
7
8
9
after
42
1
2
3
4
5
6
7
8
9

まとめ

今回は、Immutableなコレクションがどんなモノなのか、簡単にまとめてみました。System.Collections.Immutableの中には、結構いろんなモノが詰まっているので、折を見てそのあたりだとか、ロックフリーな操作をして同時実行したときのスルプットを稼ぐ方法なんかもまとめていきたいと思います。

26
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
24