Interactive Extensionsを自分で実装する – 1.Foreach

Rxからの逆輸入版としてLinq to Objectに拡張サービスとして試験リリースされている Ix(Interactive Extensions) というものがあります。以前、Linq to Objectを再実装するという海外のブログを見たことがあります、それを真似て(クオリティは高くありませんが;)勉強も兼ねてこれを私なりに実装していきたいと思います!

InteractiveExtensionsを使うには、Nugetから

PM> install-package ix-main

でokです。オブジェクトブラウザでみるとわかりますが、InteractiveExtensionsのほとんどはEnumerableExという単一クラスの静的メソッドで構成されています。

system.interactive

 

 

第1回目はForEachです。

ForEach…渡されたシーケンスに対して引数で指定したアクションを適用する。

ForEachTest.cs

namespace EmulateInteractiveExtensions.Test
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using NUnit.Framework;
    using EmulateInteractiveExtensions;

    [TestFixture]
    public class ForEachTest
    {
        [Test]
        public void Test_ForEach_SequentialAction()
        {
            var sequence = Enumerable.Range(1, 5);
            List<int> actual = new List<int>();
            List<int> expected = new List<int>(){1,2,3,4,5};

            sequence.ForEach(a => actual.Add(a));
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void Test_ForEach_SequanceAction_WithIndex()
        {
            var sequence = Enumerable.Range(2, 3);
            var actual = new List<Tuple<int,int>>();
            var expected = new List<Tuple<int, int>>();
            expected.Add(new Tuple<int, int>(2, 0));
            expected.Add(new Tuple<int, int>(3, 1));
            expected.Add(new Tuple<int, int>(4, 2));

            sequence.ForEach((a,i) => actual.Add(new Tuple<int,int>(a,i)));
            Assert.AreEqual(expected, actual);
        }
    }
}

EmulateIxExtensions.cs

namespace EmulateInteractiveExtensions
{
    using System;
    using System.Collections.Generic;

    public static class EmulateIxExtensions
    {
        public static void ForEach<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
        {
            if(source == null || action == null){
                throw new ArgumentNullException();
            }
            foreach (var item in source){
                action(item);
            }
        }

        public static void ForEach<TSource>(this IEnumerable<TSource> source, Action<TSource, int> action){
            if(source == null || action == null){
                throw new ArgumentNullException();
            }
            int i = 0;
            foreach (var item in source){
                action(item, i++);
            }
        }
    }
}

第1回目なので、補足を。

  • yield return はC#2.0の仕様です、シーケンス(IEnumerable<T>)を返すことで受け取った側はこれを使ってforeachに回すことができます。
  • 第一引数につくthisはC#3.0の仕様です、拡張メソッドといいあたかもそのクラスのpublicメソッドであるかのように扱うことができます。ちなみに拡張メソッドを初めて見たとき、後から関数を追加できるなんてカプセル化を壊すのでは??なんて思いましたが拡張メソッドで呼び出せるのはpublicに限られているし、クラスのprivate, protectedなメンバーをいじれるわけではなく、カプセル化は壊しません。
  • 各所に出てくる<T>はジェネリックというC#2.0の仕様です。C++でいうところのテンプレートで、型を引数として渡すことができます。
  • あと、他に知っておく必要があることとしてラムダ式やデリゲートがありますがこれは以前のブログを参照ください。

ForEachは渡されたシーケンスの個々のアイテムに対して、Actionで指定したデリゲートを実行するものです。

2014/4/1 引数のnullチェックを追加

 

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中