2013年3月8日金曜日

LINQ(Enumerable拡張メソッド)を利用したシンプルなリファクタリング・カタログ


VisualStudio 2008の登場以降、C#のコーディングにLINQが使えるようになり、「.netでのコーディングの仕方」がLINQの有り無しで大きく変化しました。が、今年が2013年なのですでに5年が経ちますが、まだまだLINQに馴染みが無い人も多いようです。

そこで、便利で使える局面が多いEnumerable拡張メソッドを使って、シンプルなコードを書く方法を、局面別にカタログスタイルで書いてみます。

Enumerable拡張メソッドは、以下のときに使用できます。
  • .net Framework 3.5以上を使っている。
  • System.Core.dllを参照設定している。
  • "using System.Linq;"でSystem.Link名前空間を参照している。
VisualStudio 2008以降で普通にプロジェクトを作れば、最初から上記条件を満たすプロジェクトになります。

01.コレクション内のすべての要素の合計を算出する


従来

public int Sum(IEnumerable<int> col)
{
  int sum = 0;
  foreach (int item in col)
  {
    sum += item;
  }
  return sum;
}

Enumerable+ラムダ式

public int Sum(IEnumerable<int> col)
{
  return col.Sum();
}

02.コレクション内のすべての要素の特定メンバの最小値を算出する


従来

public int Min(IEnumerable<Item> col)
{
  int min = int.MaxValue;
  foreach (Item item in col)
  {
    if (item.Cond < min)
      min = item.Cond;
  }
  return min;
}

Enumerable+ラムダ式

public int Min(IEnumerable<Item> col)
{
  return col.Select(item => item.Cond).Min();
}

03.コレクション内のすべての要素の文字数の平均を算出する


従来

public double StrLenAvg(IEnumerable<Item> col)
{
  double sum = 0.0;
  foreach (Item item in col)
  {
    sum += item.Str.Length;
  }
  return sum / col.Count();
}

Enumerable+ラムダ式

public double StrLenAvg(IEnumerable<Item> col)
{
  return col.Select(item => (double)item.Str.Length).Average();
}

04.コレクション内の条件に合致する要素の数を数える


従来

public int CountOf(IEnumerable<Item> col, int target)
{
  int count = 0;
  foreach (Item item in col)
  {
    if (item.Cond == target)
    {
      ++count;
    }
  }
  return count;
}

Enumerable+ラムダ式

public int CountOf(IEnumerable<Item> col, int target)
{
  return col.Count(item => item.Cond == target);
}

05.コレクション内にある条件に合致する要素が存在するかを確認する


従来

public bool CheckAny(IEnumerable<Item> col, int target)
{
  bool exists = false;
  foreach (Item item in col)
  {
    if (item.Cond == target)
    {
      exists = true;
      break;
    }
  }
  return exists;
}

Enumerable+ラムダ式

public bool CheckAny(IEnumerable<Item> col, int target)
{
  return col.Any(item => item.Cond == target);
}

06.コレクション内のすべての要素がある条件に合致するかを確認する


従来

public bool CheckAll(IEnumerable<Item> col, int target)
{
  bool all = true;
  foreach (Item item in col)
  {
    if (item.Cond != target)
    {
      all = false;
      break;
    }
  }
  return all;
}

Enumerable+ラムダ式

public bool CheckAll(IEnumerable<Item> col, int target)
{
  return col.All(item => item.Cond == target);
}

07.コレクション内のある条件に合致する要素のみ特定の処理を行う


従来

public void SetValueToAllOf(IEnumerable<Item> col, int target, int val)
{
  foreach (Item item in col)
  {
    if (item.Cond == target)
    {
      item.Val = val;
    }
  }
}

Enumerable+ラムダ式

public void SetValueToAllOf(IEnumerable<Item> col, int target, int val)
{
  foreach (Item item in col.Where(i => i.Cond == target))
  {
    item.Val = val;
  }
}

08.コレクション内のある条件に最初に合致する要素のみ特定の処理を行う


従来

public void SetValueToFirst(IEnumerable<Item> col, int target, int val)
{
  foreach (Item item in col)
  {
    if (item.Cond == target)
    {
      item.Val = val;
      break;
    }
  }
}

Enumerable+ラムダ式

public void SetValueToFirst(IEnumerable<Item> col, int target, int val)
{
  Item item = col.FirstOrDefault(i => i.Cond == target);
  if(item != null)
  {
    item.Val = val;
  }
}

Enumerable+ラムダ式 (特殊なケース)


col内に条件に合致するものが存在することがわかっていて、仮に存在していない場合は例外がスローされても構わない場合はこう。

public void SetValueToFirst(IEnumerable<Item> col, int target, int val)
{
  col.First(i => i.Cond == target).Val = val;
}

09.コレクション内のすべての要素の、特定メンバが最大の要素を取得する


従来

public Item MaxItemOf(IEnumerable<Item> col)
{
  Item maxitem = new Item() { Cond = int.MinValue };
  foreach (Item item in col)
  {
    if (item.Cond > maxitem.Cond)
    {
      maxitem = item;
    }
  }
  return maxitem;
}

Enumerable+ラムダ式

public Item MaxItemOf(IEnumerable<Item> col)
{
  return col.OrderByDescending(item => item.Cond).First();
}

10.コレクション内のコレクションを列挙する


従来

public string EnumLayered(IEnumerable<LayeredItem> colcol)
{
  foreach (var col in colcol)
  {
    foreach (var item in col.Items)
    {
      Console.WriteLine(item.Str);
    }
  }
}

Enumerable+ラムダ式

public string EnumLayered(IEnumerable<LayeredItem> colcol)
{
  foreach (var item in colcol.SelectMany(col => col.Items))
  {
    Console.WriteLine(item.Str);
  }
}

0 件のコメント:

コメントを投稿