2015年12月11日金曜日

.Net(VB C#) LINQ
Enumerable.TakeWhile メソッド

Enumerable.TakeWhileメソッド
【C#】
指定された条件を満たされる限り、シーケンスから要素を返した後、残りの要素をスキップします。
public static IEnumerable<TSource> TakeWhile<TSource>
    (this IEnumerable<TSource> source, Func<TSource, bool> predicate)

指定された条件が満たされる限り、シーケンスから要素を返します。要素のインデックスは、述語関数のロジックで使用されます。
public static IEnumerable TakeWhile
    (this IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
【VB】
指定された条件を満たされる限り、シーケンスから要素を返した後、残りの要素をスキップします。
<ExtensionAttribute>
Public Shared Function TakeWhile(Of TSource) 
    (source As IEnumerable(Of TSource), predicate As Func(Of TSource, Boolean))
     
指定された条件が満たされる限り、シーケンスから要素を返します。要素のインデックスは、述語関数のロジックで使用されます。
<ExtensionAttribute>
Public Shared Function TakeWhile(Of TSource) 
    (source As IEnumerable(Of TSource), predicate As Func(Of TSource, Integer, Boolean)) 
    As IEnumerable(Of TSource)


簡単に言うと…
シーケンス(配列やコレクション)の先頭から、指定された条件に該当する間その要素を返します。

このメソッドは遅延実行されます。


テストデータです。
【C#】
private class Fruit
{
    public string Name { get; set; }
    public string Rank { get; set; }
    public decimal Price { get; set; }
}

var fruits = new List<Fruit>()
                {
                    new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 600 },
                    new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 },
                    new Fruit(){Name = "りんご", Rank = "B" , Price = 800 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 500 }
                };
【VB】
Private Class Fruit
    Public Property Name As String
    Public Property Rank As String
    Public Property Price As Decimal
End Class

Dim fruits = New List(Of Fruit)() From
            {
                New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600},
                New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200},
                New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500}
            }
TakeWhileメソッドを使用して、先頭から値段が1000円以下の条件が満たされる間、その要素を取得します。
つまり、先頭から順に1000円以下かどうか判定し、1000円以下であればその要素を返します。
ぶどうが1200円ですので条件には該当しません。ここで判定はストップします。
ぶどう以降のりんごやみかんは1000円以下ですが無視されます。

【C#】
//先頭から値段が1000円以下の条件が満たされる間、その要素を取得します。
var lst1 = fruits.TakeWhile(itm => itm.Price <= 1000);
//出力
foreach (var itm in lst1)
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));  
//名称=りんご, ランク=A, 値段=1,000
//名称=みかん, ランク=A, 値段=600
【VB】
'先頭から値段が1000円以下の条件が満たされる間、その要素を取得します。
Dim lst1 = fruits.TakeWhile(Function(itm) itm.Price <= 1000)
'出力
For Each itm In lst1
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"))
Next
'名称=りんご, ランク=A, 値段=1,000
'名称=みかん, ランク=A, 値段=600
各要素のインデックスを取得できるオーバーロードメソッドもあります。
以下のコードではインデックスが3以下の間、その要素を取得します。
※Takeメソッドは引数に取得する個数を指定します。Take(3)と指定すると先頭から3つの要素を取得します。
【C#】
//インデックスが3以下の条件が満たされる間、その要素を取得します。
var lst1 = fruits.TakeWhile((itm, idx) => idx <= 3);
//出力
foreach (var itm in lst1)
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));  
//名称=りんご, ランク=A, 値段=1,000
//名称=みかん, ランク=A, 値段=600
//名称=ぶどう, ランク=B, 値段=1,200
//名称=りんご, ランク=B, 値段=800
【VB】
'インデックスが3以下の条件が満たされる間、その要素を取得します。
Dim lst1 = fruits.TakeWhile(Function(itm, idx) idx <= 3)
'出力
For Each itm In lst1
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"))
Next
'名称=りんご, ランク=A, 値段=1,000
'名称=みかん, ランク=A, 値段=600
'名称=ぶどう, ランク=B, 値段=1,200
'名称=りんご, ランク=B, 値段=800    

※VBの場合、コンパイルオプションで「Option Infer」をONにし、型推論を有効にしてください。

.Net(VB C#) LINQのメソッド一覧

.Net(VB C#) LINQ
Enumerable.Take メソッド

Enumerable.Takeメソッド
【C#】
シーケンスの先頭から、指定された数の連続する要素を返します。
public static IEnumerable<TSource> Take<TSource>
    (this IEnumerable<TSource> source, int count)
【VB】
シーケンスの先頭から、指定された数の連続する要素を返します。
<ExtensionAttribute>
Public Shared Function Take(Of TSource) 
   (source As IEnumerable(Of TSource), count As Integer) 
   As IEnumerable(Of TSource)


簡単に言うと…
シーケンス(配列やコレクション)の先頭から指定された数の要素を返します。

このメソッドは遅延実行されます。


テストデータです。
【C#】
private class Fruit
{
    public string Name { get; set; }
    public string Rank { get; set; }
    public decimal Price { get; set; }
}

var fruits = new List<Fruit>()
                {
                    new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 600 },
                    new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 },
                    new Fruit(){Name = "りんご", Rank = "B" , Price = 800 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 500 }
                };
【VB】
Private Class Fruit
    Public Property Name As String
    Public Property Rank As String
    Public Property Price As Decimal
End Class

Dim fruits = New List(Of Fruit)() From
            {
                New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600},
                New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200},
                New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500}
            }
Takeメソッドを使用して最初から3つ分の要素を取得します。
【C#】
//Takeメソッドで先頭より3つ分の要素を取得
var top3 = fruits.Take(3);
//出力
foreach(var itm in top3)
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));  
//名称=りんご, ランク=A, 値段=1,000
//名称=みかん, ランク=A, 値段=600
//名称=ぶどう, ランク=B, 値段=1,200
【VB】
'Takeメソッドで先頭より3つ分の要素を取得
Dim top3 = fruits.Take(3)
'出力
For Each itm In top3
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"))
Next
'名称=りんご, ランク=A, 値段=1,000
'名称=みかん, ランク=A, 値段=600
'名称=ぶどう, ランク=B, 値段=1,200
Takeメソッドの引数にシーケンスの要素数以上の数を指定しても、例外は発生しません。すべての要素が返ります。
Takeメソッドの引数にシーケンスの要素数以下の数を指定した場合も、例外は発生しません。空のシーケンスが返ります。
【C#】
 //要素数以上の数を指定
var lst1 = fruits.Take(9);
//出力
foreach (var itm in lst1)
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));  
//名称=りんご, ランク=A, 値段=1,000
//名称=みかん, ランク=A, 値段=600
//名称=ぶどう, ランク=B, 値段=1,200
//名称=りんご, ランク=B, 値段=800
//名称=みかん, ランク=A, 値段=500


//要素数以下の数を指定
var lst2 = fruits.Take(-9);
//出力
Console.WriteLine(lst2.Count());
//0
【VB】
'要素数以上の数を指定
Dim lst1 = fruits.Take(9)
'出力
For Each itm In lst1
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"))
Next
'名称=りんご, ランク=A, 値段=1,000
'名称=みかん, ランク=A, 値段=600
'名称=ぶどう, ランク=B, 値段=1,200
'名称=りんご, ランク=B, 値段=800
'名称=みかん, ランク=A, 値段=500


'要素数以下の数を指定
Dim lst2 = fruits.Take(-9)
'出力
Console.WriteLine(lst2.Count())
'0
※VBの場合、コンパイルオプションで「Option Infer」をONにし、型推論を有効にしてください。

.Net(VB C#) LINQのメソッド一覧

2015年12月9日水曜日

.Net(VB C#) LINQ
Enumerable.Single メソッド、SingleOrDefaultメソッド

Enumerable.Singleメソッド
【C#】
シーケンスの唯一の要素を返します。シーケンス内の要素が 1 つだけではない場合は、例外をスローします。
public static TSource Single<TSource>
    (this IEnumerable<TSource> source)
    
指定された条件を満たす、シーケンスの唯一の要素を返します。そのような要素が複数存在する場合は、例外をスローします。
public static TSource Single<TSource>
    (this IEnumerable source, Func predicate)
【VB】
シーケンスの唯一の要素を返します。シーケンス内の要素が 1 つだけではない場合は、例外をスローします。
<ExtensionAttribute>
Public Shared Function Single(Of TSource) 
    (source As IEnumerable(Of TSource)) 
    As TSource

指定された条件を満たす、シーケンスの唯一の要素を返します。そのような要素が複数存在する場合は、例外をスローします。
<ExtensionAttribute>
Public Shared Function Single(Of TSource) 
    (source As IEnumerable(Of TSource), predicate As Func(Of TSource, Boolean)) 
    As TSource

Enumerable.SingleOrDefaultメソッド
【C#】
シーケンスの唯一の要素を返します。シーケンスが空の場合、既定値を返します。
シーケンス内に要素が複数ある場合、このメソッドは例外をスローします。
public static TSource SingleOrDefault<TSource>
    (this IEnumerable<TSource> source) 
    
指定された条件を満たす、シーケンスの唯一の要素を返します。そのような要素が存在しない場合、既定値を返します。
複数の要素が条件を満たす場合、このメソッドは例外をスローします。
public static TSource SingleOrDefault<TSource>
    (this IEnumerable<TSource> source, Func<TSource, bool> predicate) 
【VB】
シーケンスの唯一の要素を返します。シーケンスが空の場合、既定値を返します。
シーケンス内に要素が複数ある場合、このメソッドは例外をスローします。
<ExtensionAttribute>
Public Shared Function SingleOrDefault(Of TSource) 
    (source As IEnumerable(Of TSource)) 
    As TSource
    
指定された条件を満たす、シーケンスの唯一の要素を返します。そのような要素が存在しない場合、既定値を返します。
複数の要素が条件を満たす場合、このメソッドは例外をスローします。
<ExtensionAttribute>
Public Shared Function SingleOrDefault(Of TSource) 
    (source As IEnumerable(Of TSource), predicate As Func(Of TSource, Boolean)) As TSource

シーケンス(配列やコレクションなど)の要素が一つだけのとき、その唯一の要素を取得するメソッドです。
Singleメソッドはシーケンスの要素が一つではないとき(つまりシーケンスの要素が0件または複数件ある状態)例外をスローします。
SingleOrDefaultメソッドはシーケンスの要素が0件のときは、デフォルト値(値型なら初期値、参照型ならnull)を返します。シーケンスの要素が複数のときは、例外をスローします。

このメソッドは即時実行されます。


SingleメソッドとFirstメソッドの使い分けについて
Firstメソッドでも1件の要素を取得することができます。
Firstメソッドは複数要素のシーケンスから、一番最初の要素を取得します。
要素が一つのシーケンスから一番最初の要素を取得すれば、唯一の要素を取得するSingleメソッドと同じ結果にはなります。
しかし後に保守する読み手には「シーケンスの要素は複数件の可能性がある」と読み取れます。
たとえFirstメソッドを使用した結果が同じであっても、シーケンスの要素が必ず1件であるということが明確なときには、Singleメソッドを使用します。
後に保守する読み手に余計な誤解を与えず「シーケンスの要素は1件だけ」ということが明確になります。


テスト用の果物クラスです。
【C#】
private class Fruit
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Rank { get; set; }
    public decimal Price { get; set; }
}
【VB】
Private Class Fruit
    Public Property ID As Integer
    Public Property Name As String
    Public Property Rank As String
    Public Property Price As Decimal
End Class
まずはFirsメソッド、FirstOrDefaultメソッドを使用して、唯一の要素を取得します。
リストの要素が1件なので、どちらも問題なく取得できます。
【C#】
//テストデータ
var fruits = new List<Fruit>()
                {
                    new Fruit(){ID = 1, Name = "りんご", Rank = "A" , Price = 1000 }
                };


//Singleメソッドを使用して唯一の要素を取得
Fruit fruit1 = fruits.Single();
//出力  
Console.WriteLine("ID={0}, 名称={1}", fruit1.ID, fruit1.Name);
//ID=1, 名称=りんご


//SingleOrDefaultメソッドを使用して唯一の要素を取得
Fruit fruit2 = fruits.SingleOrDefault();
//出力  
Console.WriteLine("ID={0}, 名称={1}", fruit2.ID, fruit2.Name);
//ID=1, 名称=りんご
【VB】
'テストデータ
Dim fruits = New List(Of Fruit)() From
    {
        New Fruit() With {.ID = 1, .Name = "りんご", .Rank = "A", .Price = 1000}
    }

'Singleメソッドを使用して唯一の要素を取得
Dim fruit1 As Fruit = fruits.Single()
'出力  
Console.WriteLine("ID={0}, 名称={1}", fruit1.ID, fruit1.Name)
'ID=1, 名称=りんご


'SingleOrDefaultメソッドを使用して唯一の要素を取得
Dim fruit2 As Fruit = fruits.SingleOrDefault()
'出力  
Console.WriteLine("ID={0}, 名称={1}", fruit2.ID, fruit2.Name)
'ID=1, 名称=りんご
次にリストの要素を複数にしてみます。
Singleメソッド、SingleOrDefaultメソッドともにInvalidOperationException例外がスローされます。
【C#】
//テストデータ
var fruits = new List<Fruit>()
                {
                    new Fruit(){ID = 1, Name = "りんご", Rank = "A" , Price = 1000 },
                    new Fruit(){ID = 2, Name = "みかん", Rank = "A" , Price = 600 }
                };


//Singleメソッドを使用して唯一の要素を取得
//例外InvalidOperationExceptionがスローされる 
Fruit fruit1 = fruits.Single();


//SingleOrDefaultメソッドを使用して唯一の要素を取得
//例外InvalidOperationExceptionがスローされる 
Fruit fruit2 = fruits.SingleOrDefault();
【VB】
'テストデータ
Dim fruits = New List(Of Fruit)() From
    {
        New Fruit() With {.ID = 1, .Name = "りんご", .Rank = "A", .Price = 1000},
        New Fruit() With {.ID = 2, .Name = "みかん", .Rank = "A", .Price = 600}
}

'Singleメソッドを使用して唯一の要素を取得
'例外InvalidOperationExceptionがスローされる 
Dim fruit1 As Fruit = fruits.Single()


'Singleメソッドを使用して唯一の要素を取得
'例外InvalidOperationExceptionがスローされる 
Dim fruit2 As Fruit = fruits.SingleOrDefault()
次はリストの要素を0件にしてみます。
SingleメソッドはInvalidOperationException例外がスローされますが
SingleOrDefaultメソッドでは、参照型の規定値であるnullが返ります。
【C#】
//テストデータ
var fruits = new List<Fruit>();


//Singleメソッドを使用して唯一の要素を取得
//例外InvalidOperationExceptionがスローされる 
Fruit fruit1 = fruits.Single();


//SingleOrDefaultメソッドを使用して唯一の要素を取得
//参照型なのでnullが返る   
Fruit fruit2 = fruits.SingleOrDefault();
//出力  
Console.WriteLine(fruit2 == null ? "null" : fruit2.Name);  
//null 
【VB】
'テストデータ
Dim fruits = New List(Of Fruit)()

'Singleメソッドを使用して唯一の要素を取得
'例外InvalidOperationExceptionがスローされる 
Dim fruit1 As Fruit = fruits.Single()


'SingleOrDefaultメソッドを使用して唯一の要素を取得
'参照型なのでnullが返る   
Dim fruit2 As Fruit = fruits.SingleOrDefault()
'出力  
Console.WriteLine(If(fruit2 Is Nothing, "nothing", fruit2.Name))
'nothing 

Singleメソッド、SingleOrDefaultメソッドには、抽出条件を指定できるオーバーロードメソッドがあります。
条件に該当するデータが1件(または0件)となるような抽出条件を指定します。
抽出条件に該当するデータが複数件の場合、Singleメソッド、SingleOrDefaultメソッドともに例外をスローします。
抽出条件に該当するデータが0件の場合、Singleメソッドは例外をスローし、SingleOrDefaultメソッドは規定値を返します。
【C#】
//テストデータ
var fruits = new List<Fruit>()  
    {  
        new Fruit(){ID = 1, Name = "りんご", Rank = "A" , Price = 1000 },  
        new Fruit(){ID = 2, Name = "みかん", Rank = "A" , Price = 600 },  
        new Fruit(){ID = 3, Name = "ぶどう", Rank = "B" , Price = 1200 },  
        new Fruit(){ID = 4, Name = "りんご", Rank = "B" , Price = 800 },  
        new Fruit(){ID = 5, Name = "みかん", Rank = "A" , Price = 500 }  
    };  

//Singleメソッドを使用して唯一の要素を取得
Fruit fruit1 = fruits.Single(itm => itm.ID == 3);
//出力
Console.WriteLine("ID={0}, 名称={1}", fruit1.ID, fruit1.Name);
//ID=3, 名称=ぶどう

//SingleOrDefaultメソッドを使用して唯一の要素を取得
Fruit fruit2 = fruits.SingleOrDefault(itm => itm.ID == 3);
//出力
Console.WriteLine("ID={0}, 名称={1}", fruit2.ID, fruit2.Name);
//ID=3, 名称=ぶどう
【VB】
'テストデータ
Dim fruits As New List(Of Fruit)() From
    {
        New Fruit() With {.ID = 1, .Name = "りんご", .Rank = "A", .Price = 1000},
        New Fruit() With {.ID = 2, .Name = "みかん", .Rank = "A", .Price = 600},
        New Fruit() With {.ID = 3, .Name = "ぶどう", .Rank = "B", .Price = 1200},
        New Fruit() With {.ID = 4, .Name = "りんご", .Rank = "B", .Price = 800},
        New Fruit() With {.ID = 5, .Name = "みかん", .Rank = "A", .Price = 500}
    }

'Singleメソッドを使用して唯一の要素を取得
Dim fruit1 As Fruit = fruits.Single(Function(itm) itm.ID = 3)
'出力
Console.WriteLine("ID={0}, 名称={1}", fruit1.ID, fruit1.Name)
'ID=3, 名称=ぶどう

'SingleOrDefaultメソッドを使用して唯一の要素を取得
Dim fruit2 As Fruit = fruits.SingleOrDefault(Function(itm) itm.ID = 3)
'出力
Console.WriteLine("ID={0}, 名称={1}", fruit2.ID, fruit2.Name)
'ID=3, 名称=ぶどう
※VBの場合、コンパイルオプションで「Option Infer」をONにし、型推論を有効にしてください。

.Net(VB C#) LINQのメソッド一覧

.Net(VB C#) LINQ
Enumerable.Last メソッド、LastOrDefaultメソッド

Enumerable.Lastメソッド
【C#】
シーケンスの最後の要素を返します。
public static TSource Lastt<TSource>
    (this IEnumerable<TSource> source)

指定された条件を満たす、シーケンスの最後の要素を返します。
public static TSource Last<TSource>
   (this IEnumerable<TSource> source, Func<TSource, bool> predicate)
【VB】
シーケンスの最後の要素を返します。
<ExtensionAttribute>
Public Shared Function Last(Of TSource) 
    (source As IEnumerable(Of TSource)) 
    As TSource

指定された条件を満たす、シーケンスの最後の要素を返します。
<ExtensionAttribute>
Public Shared Function Last(Of TSource) 
    (source As IEnumerable(Of TSource), predicate As Func(Of TSource, Boolean)) 
    As TSource

Enumerable.LastOrDefaultメソッド
【C#】
シーケンスの最後の要素を返します。シーケンスに要素が含まれていない場合は既定値を返します。
public static TSource LastOrDefault<TSource>
    (this IEnumerable<TSource> source)
    
条件を満たす、シーケンスの最後の要素を返します。このような要素が見つからない場合は既定値を返します。
public static TSource LastOrDefault
    (this IEnumerable<TSource> source, Func predicate)
【VB】
シーケンスの最後の要素を返します。シーケンスに要素が含まれていない場合は既定値を返します。
<ExtensionAttribute>
Public Shared Function LastOrDefault(Of TSource) 
    (source As IEnumerable(Of TSource)) 
    As TSource

条件を満たす、シーケンスの最後の要素を返します。このような要素が見つからない場合は既定値を返します。
<ExtensionAttribute>
Public Shared Function LastOrDefault(Of TSource) 
    (source As IEnumerable(Of TSource), predicate As Func(Of TSource, Boolean)) 
    As TSource

シーケンス(配列やコレクション)から一番最後の要素を取得します。
Lastメソッドは一番最後の要素がなければ(つまりシーケンスにデータがない状態)例外をスローします。
LastOrDefaultメソッドは一番最後の要素がなければデフォルト値(値型なら初期値、参照型ならnull)を返します。

このメソッドは即時実行されます。


テストデータです。
【C#】
private class Fruit
{
    public string Name { get; set; }
    public string Rank { get; set; }
    public decimal Price { get; set; }
}

var fruits = new List<Fruit>()
                {
                    new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 600 },
                    new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 },
                    new Fruit(){Name = "りんご", Rank = "B" , Price = 800 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 500 }
                };
【VB】
Private Class Fruit
    Public Property Name As String
    Public Property Rank As String
    Public Property Price As Decimal
End Class

Dim fruits = New List(Of Fruit)() From
            {
                New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600},
                New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200},
                New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500}
            }
まずはLastメソッド、LastOrDefaultメソッドを使用して、一番最後の要素を取得します。
一番最後の要素は存在するので、どちらも問題なく取得できます。
【C#】
//Lastメソッドを使用して、一番最後の要素を取得
Fruit fruit1 = fruits.Last();
//出力
Console.WriteLine(fruit1.Name);
//みかん


//LastOrDefaultメソッドを使用して、一番最後の要素を取得
Fruit fruit2 = fruits.LastOrDefault();
//出力
Console.WriteLine(fruit2.Name);
//みかん
【VB】
'Firstメソッドを使用して、一番最後の要素を取得
Dim fruit1 As Fruit = fruits.Last()
'出力
Console.WriteLine(fruit1.Name)
'みかん

'LastOrDefaultメソッドを使用して、一番最後の要素を取得
Dim fruit2 As Fruit = fruits.LastOrDefault()
'出力
Console.WriteLine(fruit2.Name)
'みかん
次にリストのデータを削除して、存在しない一番最後の要素を取得してみます。
LastメソッドはInvalidOperationException例外がスローされるますが、
LastOrDefaultメソッドでは、参照型の規定値であるnullが返ります。
【C#】
//リストからデータを削除
fruits.Clear();

//Lastメソッドを使用して、一番最後の要素を取得
//例外InvalidOperationExceptionがスローされる 
Fruit fruit1 = fruits.Last();


//LastOrDefaultメソッドを使用して、一番最後の要素を取得
//参照型なのでnullが返る 
Fruit fruit2 = fruits.LastOrDefault();
//出力
Console.WriteLine(fruit2 == null ? "null" : fruit2.Name);
//null
【VB】
'リストからデータを削除
fruits.Clear()

'Lastメソッドを使用して、一番最後の要素を取得
'例外InvalidOperationExceptionがスローされる 
Dim fruit1 As Fruit = fruits.Last()


'LastOrDefaultメソッドを使用して、一番最後の要素を取得
'参照型なのでnullが返る 
Dim fruit2 As Fruit = fruits.LastOrDefault()
'出力
Console.WriteLine(If(fruit2 Is Nothing, "nothing", fruit2.Name))
'nothing

Lastメソッド、LastOrDefaultメソッドには、抽出条件を指定できるオーバーロードメソッドがあります。
抽出条件を指定すると、抽出条件に該当したデータの一番最後の要素を取得できます。
該当するデータがない場合、Lastメソッドは例外をスローし、LastOrDefaultメソッドは規定値を返します。
【C#】
//Lastメソッドを使用して、抽出条件に該当する一番最後の要素を取得
Fruit fruit1 = fruits.Last( itm => itm.Name == "りんご");
//出力
Console.WriteLine("名称={0}, ランク={1}, 値段={2}", fruit1.Name, fruit1.Rank, fruit1.Price.ToString("#,##0"));
//名称=りんご, ランク=B, 値段=800


//LastOrDefaultメソッドを使用して、抽出条件に該当する一番最後の要素を取得
Fruit fruit2 = fruits.LastOrDefault(itm => itm.Name == "みかん");
//出力
Console.WriteLine("名称={0}, ランク={1}, 値段={2}", fruit2.Name, fruit2.Rank, fruit2.Price.ToString("#,##0"));
//名称=みかん, ランク=A, 値段=500
【VB】
'Lastメソッドを使用して、一番最後の要素を取得
Dim fruit1 As Fruit = fruits.Last(Function(itm) itm.Name = "りんご")
'出力
Console.WriteLine("名称={0}, ランク={1}, 値段={2}", fruit1.Name, fruit1.Rank, fruit1.Price.ToString("#,##0"))
'名称=りんご, ランク=B, 値段=800

'LastOrDefaultメソッドを使用して、一番最後の要素を取得
Dim fruit2 As Fruit = fruits.LastOrDefault(Function(itm) itm.Name = "みかん")
'出力
Console.WriteLine("名称={0}, ランク={1}, 値段={2}", fruit2.Name, fruit2.Rank, fruit2.Price.ToString("#,##0"))
'名称=みかん, ランク=A, 値段=500
※VBの場合、コンパイルオプションで「Option Infer」をONにし、型推論を有効にしてください。

.Net(VB C#) LINQのメソッド一覧

.Net(VB C#) LINQ
Enumerable.First メソッド、FirstOrDefaultメソッド

Enumerable.Firstメソッド
【C#】
シーケンスの最初の要素を返します。
public static TSource First<TSource>
    (this IEnumerable<TSource> source)

指定された条件を満たす、シーケンスの最初の要素を返します。
public static TSource First<TSource>
   (this IEnumerable<TSource> source, Func<TSource, bool> predicate)
【VB】
シーケンスの最初の要素を返します。
<ExtensionAttribute>
Public Shared Function First(Of TSource) 
    (source As IEnumerable(Of TSource)) 
    As TSource

指定された条件を満たす、シーケンスの最初の要素を返します。
<ExtensionAttribute>
Public Shared Function First(Of TSource) 
    (source As IEnumerable(Of TSource), predicate As Func(Of TSource, Boolean)) 
    As TSource

Enumerable.FirstOrDefaultメソッド
【C#】
シーケンスの最初の要素を返します。シーケンスに要素が含まれていない場合は既定値を返します。
public static TSource FirstOrDefault<TSource>
    (this IEnumerable<TSource> source)
    
条件を満たす、シーケンスの最初の要素を返します。このような要素が見つからない場合は既定値を返します。
public static TSource FirstOrDefault
    (this IEnumerable<TSource> source, Func predicate)
【VB】
シーケンスの最初の要素を返します。シーケンスに要素が含まれていない場合は既定値を返します。
<ExtensionAttribute>
Public Shared Function FirstOrDefault(Of TSource) 
    (source As IEnumerable(Of TSource)) 
    As TSource

条件を満たす、シーケンスの最初の要素を返します。このような要素が見つからない場合は既定値を返します。
<ExtensionAttribute>
Public Shared Function FirstOrDefault(Of TSource) 
    (source As IEnumerable(Of TSource), predicate As Func(Of TSource, Boolean)) 
    As TSource

シーケンス(配列やコレクションなど)から一番最初の要素を取得します。
Firsttメソッドは一番最初の要素がなければ(つまりシーケンスにデータがない状態)例外をスローします。
FirstOrDefaultメソッドは一番最初の要素がなければデフォルト値(値型なら初期値、参照型ならnull)を返します。

このメソッドは即時実行されます。


テストデータです。
【C#】
private class Fruit
{
    public string Name { get; set; }
    public string Rank { get; set; }
    public decimal Price { get; set; }
}

var fruits = new List<Fruit>()
                {
                    new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 600 },
                    new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 },
                    new Fruit(){Name = "りんご", Rank = "B" , Price = 800 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 500 }
                };
【VB】
Private Class Fruit
    Public Property Name As String
    Public Property Rank As String
    Public Property Price As Decimal
End Class

Dim fruits = New List(Of Fruit)() From
            {
                New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600},
                New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200},
                New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500}
            }
まずはFirstメソッド、FirstOrDefaultメソッドを使用して、一番最初の要素を取得します。
一番最初の要素は存在するので、どちらも問題なく取得できます。
【C#】
//Firstメソッドを使用して、一番最初の要素を取得
Fruit fruit1 = fruits.First();
//出力
Console.WriteLine(fruit1.Name);
//りんご


//FirstOrDefaultメソッドを使用して、一番最初の要素を取得
Fruit fruit2 = fruits.FirstOrDefault();
//出力
Console.WriteLine(fruit2.Name);
//りんご
【VB】
'Firstメソッドを使用して、一番最初の要素を取得
Dim fruit1 As Fruit = fruits.First()
'出力
Console.WriteLine(fruit1.Name)
'りんご

'FirstOrDefaultメソッドを使用して、一番最初の要素を取得
Dim fruit2 As Fruit = fruits.FirstOrDefault()
'出力
Console.WriteLine(fruit2.Name)
'りんご
次にリストのデータを削除して、存在しない一番最初の要素を取得してみます。
FirstメソッドはInvalidOperationException例外がスローされるますが、
FirstOrDefaultメソッドでは、参照型の規定値であるnullが返ります。
【C#】
//リストからデータを削除
fruits.Clear();

//Firstメソッドを使用して、一番最初の要素を取得
//例外InvalidOperationExceptionがスローされる 
Fruit fruit1 = fruits.First();


//FirstOrDefaultメソッドを使用して、一番最初の要素を取得
//参照型なのでnullが返る 
Fruit fruit2 = fruits.FirstOrDefault();
//出力
Console.WriteLine(fruit2 == null ? "null" : fruit2.Name);
//null
【VB】
'リストからデータを削除
fruits.Clear()

'Firstメソッドを使用して、一番最初の要素を取得
'例外InvalidOperationExceptionがスローされる 
Dim fruit1 As Fruit = fruits.First()


'FirstOrDefaultメソッドを使用して、一番最初の要素を取得
'参照型なのでnullが返る 
Dim fruit2 As Fruit = fruits.FirstOrDefault()
'出力
Console.WriteLine(If(fruit2 Is Nothing, "nothing", fruit2.Name))
'nothing

Firstメソッド、FirstOrDefaultメソッドには、抽出条件を指定できるオーバーロードメソッドがあります。
抽出条件を指定すると、抽出条件に該当したデータの一番最初の要素を取得できます。
該当するデータがない場合、Firstメソッドは例外をスローし、FirstOrDefaultメソッドは規定値を返します。
【C#】
//Firstメソッドを使用して、抽出条件に該当する一番最初の要素を取得
Fruit fruit1 = fruits.First( itm => itm.Name == "りんご");
//出力
Console.WriteLine("名称={0}, ランク={1}, 値段={2}", fruit1.Name, fruit1.Rank, fruit1.Price.ToString("#,##0"));
//名称=りんご, ランク=A, 値段=1,000


//FirstOrDefaultメソッドを使用して、抽出条件に該当する一番最初の要素を取得
Fruit fruit2 = fruits.FirstOrDefault(itm => itm.Name == "みかん");
//出力
Console.WriteLine("名称={0}, ランク={1}, 値段={2}", fruit2.Name, fruit2.Rank, fruit2.Price.ToString("#,##0"));
//名称=みかん, ランク=A, 値段=600
【VB】
'Firstメソッドを使用して、一番最初の要素を取得
Dim fruit1 As Fruit = fruits.First(Function(itm) itm.Name = "りんご")
'出力
Console.WriteLine("名称={0}, ランク={1}, 値段={2}", fruit1.Name, fruit1.Rank, fruit1.Price.ToString("#,##0"))
'名称=りんご, ランク=A, 値段=1,000

'FirstOrDefaultメソッドを使用して、一番最初の要素を取得
Dim fruit2 As Fruit = fruits.FirstOrDefault(Function(itm) itm.Name = "みかん")
'出力
Console.WriteLine("名称={0}, ランク={1}, 値段={2}", fruit2.Name, fruit2.Rank, fruit2.Price.ToString("#,##0"))
'名称=みかん, ランク=A, 値段=600
※VBの場合、コンパイルオプションで「Option Infer」をONにし、型推論を有効にしてください。

.Net(VB C#) LINQのメソッド一覧

2015年12月7日月曜日

.Net(VB C#) LINQ
Enumerable.ElementAt メソッド、ElementAtOrDefaultメソッド

Enumerable.ElementAtメソッド
【C#】
シーケンス内の指定されたインデックス位置にある要素を返します。
public static TSource ElementAt<TSource>
    (this IEnumerable<TSource> source, int index)
【VB】
シーケンス内の指定されたインデックス位置にある要素を返します。
<ExtensionAttribute>
Public Shared Function ElementAt(Of TSource) 
    (source As IEnumerable(Of TSource), index As Integer) 
    As TSource

Enumerable.ElementAtOrDefaultメソッド
【C#】
シーケンス内の指定されたインデックス位置にある要素を返します。インデックスが範囲外の場合は既定値を返します。
public static TSource ElementAtOrDefault<TSource>
   (this IEnumerable source, int index)
【VB】
シーケンス内の指定されたインデックス位置にある要素を返します。インデックスが範囲外の場合は既定値を返します。
<ExtensionAttribute>
Public Shared Function ElementAtOrDefault(Of TSource) 
    (source As IEnumerable(Of TSource), index As Integer) 
    As TSource

シーケンス(配列やコレクションなど)からインデックスを指定して要素を取得します。
ElementAtメソッドは指定したインデックスの要素がなければ例外をスローします。
ElementAtOrDefaultメソッドは指定したインデックスの要素がなければデフォルト値(値型なら初期値、参照型ならnull)を返します。

このメソッドは即時実行されます。


テストデータです。
【C#】
private class Fruit
{
    public string Name { get; set; }
    public string Rank { get; set; }
    public decimal Price { get; set; }
}

var fruits = new List<Fruit>()
                {
                    new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 600 },
                    new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 },
                    new Fruit(){Name = "りんご", Rank = "B" , Price = 800 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 500 }
                };
【VB】
Private Class Fruit
    Public Property Name As String
    Public Property Rank As String
    Public Property Price As Decimal
End Class

Dim fruits = New List(Of Fruit)() From
            {
                New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600},
                New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200},
                New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500}
            }
まずはElementAtメソッド、ElementAtOrDefaultメソッドを使用してインデックス0番目の要素を取得します。
0番目の要素は存在するので、どちらも問題なく取得できます。
【C#】
//ElementAtメソッドを使用して、インデックス0番目の要素を取得
Fruit fruit1 = fruits.ElementAt(0);
//出力
Console.WriteLine(fruit1.Name);
//りんご


//ElementAtOrDefaultメソッドを使用して、インデックス0番目の要素を取得
Fruit fruit2 = fruits.ElementAtOrDefault(0);
//出力
Console.WriteLine(fruit2.Name);
//りんご
【VB】
'ElementAtメソッドを使用して、インデックス0番目の要素を取得
Dim fruit1 As Fruit = fruits.ElementAt(0)
'出力
Console.WriteLine(fruit1.Name)
'りんご

'ElementAtOrDefaultメソッドを使用して、インデックス0番目の要素を取得
Dim fruit2 As Fruit = fruits.ElementAtOrDefault(0)
'出力
Console.WriteLine(fruit2.Name)
'りんご
次に存在しないインデックス5番目の要素を取得してみます。
ElementAtメソッドはArgumentOutOfRangeException例外がスローされるますが、
ElementAtOrDefaultメソッドでは、参照型の規定値であるnullが返ります。
【C#】
//ElementAtメソッドを使用して、インデックス5番目の要素を取得
//ArgumentOutOfRangeException例外がスローされる
Fruit fruit1 = fruits.ElementAt(5);

//ElementAtメソッドを使用して、インデックス5番目の要素を取得
//参照型なのでnullが返る
Fruit fruit2 = fruits.ElementAtOrDefault(5);
//出力
Console.WriteLine(fruit2 == null ? "null" : fruit2.Name);
//null
【VB】
'ElementAtメソッドを使用して、インデックス5番目の要素を取得
'ArgumentOutOfRangeException例外がスローされる
Dim fruit1 As Fruit = fruits.ElementAt(5)

'ElementAtメソッドを使用して、インデックス5番目の要素を取得
'参照型なのでnullが返る
Dim fruit2 As Fruit = fruits.ElementAtOrDefault(5)
'出力
Console.WriteLine(If(fruit2 Is Nothing, "nothing", fruit2.Name))
'nothing
※VBの場合、コンパイルオプションで「Option Infer」をONにし、型推論を有効にしてください。

.Net(VB C#) LINQのメソッド一覧

C# と VB に思うこと・・・

2003年から2014年まで11年間ず~とVBでした。
.Net2003(Fw1.1)から.Net2010(.Fw3.0)までの開発でした。

2015年の1年間はC#でした。
.Net2003(Fw1.1)と.Net2010(Fw4.0)でした。

来年からはVBです。
きっともうC#に戻れる日は来ないです。

こんなどっぷりVBに浸かった私からC#erとVBerに伝えたいことがあります。


拝啓
VBerのみなさん

VBでの開発は楽しいですか?
C#erに下に見られるけれど、VBだってそんなに悪くないと思ってますよね。
現にC#でできることはVBでも大方できますしね。
プログラムの良し悪しなんて、プログラムの書き手によるものだと思ってますよね。
VBerでも綺麗と書く奴はいると。自分は綺麗にかいているほうだと。

私もそう思ってました。
でもねたった1年C#で開発しただけなんですけどね、

圧倒的にC#の方が書きやすいです!!

圧倒的にC#の方が楽ですよ!!

なんならC#の方が簡単ですよ!!

そんなにVBに固執しなければいけない理由がないなら、一度C#で開発してみることをお勧めします。
変数宣言の順番と、文末のセミコロンと、中括弧にさえ慣れれば、あとはVBで使っていた余計なキーワードを削除していくだけです。

タイプ量は大幅に少なくなります。
C#に慣れると、VBで打たなければならないキーワードを覚えるのも打つのも冗長でメンドクサイです。

そんなに頑なにC#は分からないって思わなくても大丈夫ですよ。
VBより覚えることは少ないですから。
数か月、C#で開発すれば自然と手が覚えます!
C#は経験ない?ホントウに大丈夫ですってば!!

VB6でずっと開発していたらから.NetでもVBでって思ったんですよね。
でもねC#の方がサンプルコードが多いんです。
わからなくなったとき、ちょっとググれば情報はヒットしますよ。 そのコードをVBに翻訳する知識と手間の方が大変ですよ? 長くVBで開発してきましたけど、C#が選べる環境にあるのなら、私は断然 C# をお勧めします!! 以上です。 私はVBerを応援しています。



拝啓
C#erのみなさん

いつもVBの事を下に見てますが、VBerから一言申し上げたいことがあります。

C#の開発は楽しいですね。
私もC#が大好きです。VBよりも好きです。
今後はC#の開発は巡りあえそうになく、定年までVBでの開発になるかと思うと、頭をかきむしり身を引き裂かれる思いです。
しかし、家庭がある身では言語一つの違いに会社を変えるような文句を言ってはいられませんので
VBであってもC#erにバカにされないよう、綺麗なコードを書いていこうと誓いを新たにしております。

またC#erには日々勉強熱心な方々が多く、有益な内容をブログにアップしていただいたりと、日々大変勉強になっております。
この場をお借りしてお礼申し上げます。
本当にありがとうございます。

ただすぐれたC#erがいらっしゃる一方、すぐれていないC#erもいらっしゃるのも事実です。
ご存知でしたか?
私も以前はC#であればもっと素敵なシステムができあがるんだろうなぁと漠然とした憧れを抱いておりました。
しかし現実は酷いものでした。
VB6をC#に書き写しただけのシステムだったのです!!
あり得ませんでした。
たまたまそのシステムが悪かっただけかもしれません。
しかし新人にもC#とはこういう書き方をするものだと、脈々と受け継がれていっておりましたのでもうしばらくは酷い状態が続くのでしょう。
また悔しいことに、VBerである私は彼らから「VB?( ´,_ゝ`)プッ」とされました。

VBだから酷いコードを書く。できあがるシステムも酷い。
C#だから美しいコードを書く。できあがるシステムも素敵。
ではないのですよね。
みなさんもわかってると思うんです。
VBでもよいコードを書きよいシステムを作ろうとしている人がいることを。
もうVBをプッってするのやめてもらえませんか?

あとC#erには「俺ってできる奴」とおもっている方が多いですよね。
コメント一切書かず、「俺のコードは短くてコメントなくても読めるだろっ え? 読めないのかよ。 それはお前の勉強不足だぜ」的な方です。
そんな事は一切言ってない?
コードからプンプン臭ってくるんです。「オレッテスゴイゼ」臭が。
勘弁して頂けないでしょうか。
申し訳ございませんが会社でのプログラム作成は「仕事」です。趣味のプログラムではありません。
保守する人は新人もいます。
新人が読めないから、あなたのコードが私に回ってくるんです。
そんな聞いたこともないような英単語の変数名、ググるのメンドクサイんです。
意味がわからない1字から3字の略語で変数名を付けられても、カッコいいのかもしれませんが、私には何の略語なのかわかりません。

あなたが出来る男だということは認めます。
知識も豊富ですし勉強熱心です。スゴイです。
でもアナタのコード…読みづらいですよ。
しかもあなたは人に教えるのは嫌いですよね?
自分が勉強した努力を人に易々とは教えたくないのでしょうか?
気持ちはわからなくもないですが、出来る男の割には器がちっちゃいですね。

不思議な事にC#erには「俺ってできる奴」的な方が結構いらっしゃるんですが
VBerには「俺ってできる奴」的な方はかなり少ないです。
よくできる方も、謙虚でいらっしゃる。
「俺ってできる奴」的な方はきっとC#に流れてくれたのでしょう。ありがたいことです。

この点についてもC#erの方々にはお礼申し上げなければいけません。
本当にありがとうございます。

以上です。
私はすぐれた器の大きいC#erのみを尊敬しています。

2015年12月6日日曜日

.Net(VB C#) LINQ
Enumerable.Cast メソッド、OfType メソッド

Enumerable.Castメソッド
【C#】
IEnumerable の要素を、指定した型にキャストします。
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
【VB】
IEnumerable の要素を、指定した型にキャストします。
<ExtensionAttribute>
Public Shared Function Cast(Of TResult) (source As IEnumerable) As IEnumerable(Of TResult)

Enumerable.OfTypeメソッド
【C#】
指定された型に基づいて IEnumerable の要素をフィルター処理します。
public static IEnumerable OfType(this IEnumerable source)
【VB】
指定された型に基づいて IEnumerable の要素をフィルター処理します。
<ExtensionAttribute>
Public Shared Function OfType(Of TResult) (source As IEnumerable) As IEnumerable(Of TResult)

シーケンス(配列やコレクションなど)の要素を指定した型にキャストし、その型のシーケンスを返します。
Castメソッドは要素が指定した型にキャストできないときに例外をスローします。
OfTypeメソッドは要素が指定した型にキャストできるもののみを抽出します。

ArrayListなど非ジェネリックなコレクションを、指定した型のIEnumerableにできるので、
Linqを利用できないコレクションをLinqで操作したいときに利用します。

このメソッドは遅延実行されます。


テストデータです。
親クラスと、親クラスを継承した子クラスを作成します。
親クラスのみを格納したArrayListと
親クラスと子クラスをごちゃ混ぜに格納したArrayListを作成します。
【C#】
//親クラスの定義
private class ParentClass
{
    public string ParentName { set; get; }
}
//子クラスの定義
private class ChildClass : ParentClass
{
    public string ChildName { set; get; }
}


//親クラスのみを格納したArrayListを作成
var aryOnlyParent = new System.Collections.ArrayList() 
    { 
        new ParentClass() {ParentName = "parent1"},
        new ParentClass() {ParentName = "parent2"},
        new ParentClass() {ParentName = "parent3"}
    };
//親クラスと子クラスをゴチャ混ぜに格納したArrayListを作成
var aryMix = new System.Collections.ArrayList()
    {
        new ParentClass() {ParentName = "parent1"},
        new ChildClass() {ParentName = "parent2", ChildName="child1"},
        null
    };
【VB】
'親クラスの定義
Private Class ParentClass
    Public Property ParentName As String
End Class
'子クラスの定義
Private Class ChildClass : Inherits ParentClass
    Public Property ChildName As String
End Class

'親クラスのみを格納したArrayListを作成
Dim aryOnlyParent = New System.Collections.ArrayList() From
        {
            New ParentClass() With {.ParentName = "parent1"},
            New ParentClass() With {.ParentName = "parent2"},
            New ParentClass() With {.ParentName = "parent3"}
        }
'親クラスと子クラスをゴチャ混ぜに格納したArrayListを作成
Dim aryMix = New System.Collections.ArrayList() From
        {
            New ParentClass() With {.ParentName = "parent1"},
            New ChildClass() With {.ParentName = "parent2", .ChildName = "child1"},
            Nothing
        }
親クラスのみを格納したArrayListの要素を、Castメソッドを使用して親クラスに変換します。
特に問題なく実行できます。
【C#】
//Cast<ParentClass>メソッドで、ArrayListの要素をParentClassにキャストする。
//すべてParentClassのArrayListであれば実行できる
var castList1 = aryOnlyParent.Cast<ParentClass>();

//出力
foreach (var itm in castList1)
    Console.WriteLine(itm.ParentName);
//parent1
//parent2
//parent3
【VB】
'Cast(Of ParentClass)メソッドで、ArrayListの要素をParentClassにキャストする。
'すべてParentClassのArrayListであれば実行できる
Dim castList1 = aryOnlyParent.Cast(Of ParentClass)()
'出力
For Each itm In castList1
    Console.WriteLine(itm.ParentName)
Next
'parent1
'parent2
'parent3
次に親クラス子クラスをゴチャ混ぜに格納したArrayListの要素を、Castメソッドを使用して子クラスに変換します。
子クラスに変換できないものがあるので例外がスローされます。
【C#】
//Cast<ChildClass>メソッドで、ArrayListの要素をChildClassにキャストする。
//ParentClassとChildClassのごちゃ混ぜArrayListは例外がスローされる
var castList2 = aryMix.Cast<ChildClass>();

//出力
foreach (var itm in castList2)
    Console.WriteLine(itm.ParentName);
//ParentClassとnullはChildClassにキャストできないので例外をスローする
【VB】
'Cast(Of ChildClass)メソッドで、ArrayListの要素をChildClassにキャストする。
'ParentClassとChildClassのごちゃ混ぜArrayListは例外がスローされる
Dim castList2 = aryMix.Cast(Of ChildClass)()

'出力
For Each itm In castList2
    Console.WriteLine(itm.ParentName)
Next
'ChildClassとnullはChildClassにキャストできないので例外をスローする
親クラス子クラスをゴチャ混ぜに格納したArrayListの要素を、TypeOfメソッドを使用して子クラスに変換します。
子クラスに変換できないものは除外されます。
【C#】
//ParentClassとChildClassのごちゃ混ぜArrayListを
var oftypeList = aryMix.OfType<ChildClass>();

//出力
foreach (var itm in oftypeList)
    Console.WriteLine(itm.ParentName);
//parent2
【VB】
'ParentClassとChildClassのごちゃ混ぜArrayListを
Dim oftypeList = aryMix.OfType(Of ChildClass)()

'出力
For Each itm In oftypeList
    Console.WriteLine(itm.ParentName)
Next
'parent2
※VBの場合、コンパイルオプションで「Option Infer」をONにし、型推論を有効にしてください。

.Net(VB C#) LINQのメソッド一覧

2015年12月4日金曜日

.Net(VB C#) LINQ
Enumerable.OrderByDescending メソッド、ThenByDescending メソッド

Enumerable.OrderByDescending メソッド
【C#】
シーケンスの要素をキーに従って降順に並べ替えます。
public static IOrderedEnumerable OrderByDescending<TSource, TKey>
    (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)

指定された比較子を使用してシーケンスの要素を降順に並べ替えます。
public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>
    (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
【VB】
シーケンスの要素をキーに従って降順に並べ替えます。
<ExtensionAttribute>
Public Shared Function OrderByDescending(Of TSource, TKey) 
    (source As IEnumerable(Of TSource), keySelector As Func(Of TSource, TKey)) 
    As IOrderedEnumerable(Of TSource)

指定された比較子を使用してシーケンスの要素を降順に並べ替えます。
<ExtensionAttribute>
Public Shared Function OrderByDescending(Of TSource, TKey) 
    (source As IEnumerable(Of TSource), keySelector As Func(Of TSource, TKey), comparer As IComparer(Of TKey)) 
    As IOrderedEnumerable(Of TSource)

Enumerable.ThenByDescending メソッド
【C#】
キーに従って、シーケンス内の後続の要素を降順で配置します。
public static IOrderedEnumerable<TSource> ThenByDescending<TSource, TKey>
    (this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector)

指定された比較子を使用して、シーケンス内の後続の要素を降順で配置します。
public static IOrderedEnumerable<TSource> ThenByDescending<TSource, TKey>
    (this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
【VB】
キーに従って、シーケンス内の後続の要素を降順で配置します。
<ExtensionAttribute>
Public Shared Function ThenByDescending(Of TSource, TKey) 
    (source As IOrderedEnumerable(Of TSource), keySelector As Func(Of TSource, TKey)) 
    As IOrderedEnumerable(Of TSource)


指定された比較子を使用して、シーケンス内の後続の要素を降順で配置します。
<ExtensionAttribute>
Public Shared Function ThenByDescending(Of TSource, TKey) 
    (source As IOrderedEnumerable(Of TSource), keySelector As Func(Of TSource, TKey), comparer As IComparer(Of TKey)) 
    As IOrderedEnumerable(Of TSource)

簡単に言うと…
シーケンス(配列やコレクションなど)の各要素を降順に並び替える。

このメソッドは遅延実行されます。


OrderByメソッド、ThenByメソッドと使い方は同じなので、サンプルコードは割愛します。
OrderByメソッド、ThenByメソッドの使い方はコチラ
.Net(VB C#) LINQ Enumerable.OrderBy メソッド、ThenBy メソッド

.Net(VB C#) LINQのメソッド一覧

.Net(VB C#) LINQ
Enumerable.OrderBy メソッド、ThenBy メソッド

Enumerable.OrderBy メソッド
【C#】
シーケンスの要素をキーに従って昇順に並べ替えます。
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>
    (this IEnumerable source, Func keySelector)


指定された比較子を使用してシーケンスの要素を昇順に並べ替えます。
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>:
    (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
【VB】
シーケンスの要素をキーに従って昇順に並べ替えます。
<ExtensionAttribute>
Public Shared Function OrderBy(Of TSource, TKey) 
    (source As IEnumerable(Of TSource), keySelector As Func(Of TSource, TKey)) 
    As IOrderedEnumerable(Of TSource)

指定された比較子を使用してシーケンスの要素を昇順に並べ替えます。
<ExtensionAttribute>
Public Shared Function OrderBy(Of TSource, TKey) 
    (source As IEnumerable(Of TSource), keySelector As Func(Of TSource, TKey), comparer As IComparer(Of TKey))
    As IOrderedEnumerable(Of TSource)

Enumerable.ThenBy メソッド
【C#】
キーに従って、シーケンス内の後続の要素を昇順で配置します。
ThenBy<TSource, TKey>(IOrderedEnumerable<TSource>, Func<TSource, TKey>)

指定された比較子を使用して、シーケンス内の後続の要素を昇順で配置します。
ThenBy<TSource, TKey>(IOrderedEnumerable<TSource>, Func<TSource, TKey>, IComparer<TKey>)
【VB】
キーに従って、シーケンス内の後続の要素を昇順で配置します。
ThenBy(Of TSource, TKey)(IOrderedEnumerable(Of TSource), Func(Of TSource, TKey))

指定された比較子を使用して、シーケンス内の後続の要素を昇順で配置します。
ThenBy(Of TSource, TKey)(IOrderedEnumerable(Of TSource), Func(Of TSource, TKey), IComparer(Of TKey))

簡単に言うと…
シーケンス(配列やコレクションなど)の各要素を昇順に並び替える。

データを並び替える項目が一つ目ならOrderByを使用しします。
二つ目からはThenByを使用します。

このメソッドは遅延実行されます。


テストデータです。
【C#】
private class Fruit
{
    public string Name { get; set; }
    public string Rank { get; set; }
    public decimal Price { get; set; }
}

var fruits = new List<Fruit>()
                {
                    new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 600 },
                    new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 },
                    new Fruit(){Name = "りんご", Rank = "B" , Price = 800 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 500 }
                };
【VB】
Private Class Fruit
    Public Property Name As String
    Public Property Rank As String
    Public Property Price As Decimal
End Class

Dim fruits = New List(Of Fruit)() From
            {
                New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600},
                New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200},
                New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500}
            }

果物リストを名前で昇順に並び替えます。

【C#】
//名称を昇順で並び替え
var ordered = fruits.OrderBy(itm => itm.Name);

//出力
foreach (var itm in ordered)
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));  
//名称=ぶどう, ランク=B, 値段=1,200
//名称=みかん, ランク=A, 値段=600
//名称=みかん, ランク=A, 値段=500
//名称=りんご, ランク=A, 値段=1,000
//名称=りんご, ランク=B, 値段=800
【VB】
'名称を昇順で並び替え
Dim ordered = fruits.OrderBy(Function(itm) itm.Name)

'出力
For Each itm In ordered
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"))
Next
'名称=ぶどう, ランク=B, 値段=1,200
'名称=みかん, ランク=A, 値段=600
'名称=みかん, ランク=A, 値段=500
'名称=りんご, ランク=A, 値段=1,000
'名称=りんご, ランク=B, 値段=800

果物リストを名前の昇順、値段の昇順に並び替えます。
一つ目のソート条件である名前はOrderByメソッドで指定し、二つ目のソート条件である値段はThenByメソッドで指定します。

【C#】
//名称の昇順、値段の昇順で並び替え
var ordered = fruits.OrderBy(itm => itm.Name).ThenBy(itm => itm.Price);

//出力
foreach (var itm in ordered)
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));  
//名称=ぶどう, ランク=B, 値段=1,200
//名称=みかん, ランク=A, 値段=500
//名称=みかん, ランク=A, 値段=600
//名称=りんご, ランク=B, 値段=800
//名称=りんご, ランク=A, 値段=1,000
【VB】
'名称の昇順、値段の昇順で並び替え
Dim ordered = fruits.OrderBy(Function(itm) itm.Name).ThenBy(Function(itm) itm.Price)

'出力
For Each itm In ordered
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"))
Next
'名称=ぶどう, ランク=B, 値段=1,200
'名称=みかん, ランク=A, 値段=500
'名称=みかん, ランク=A, 値段=600
'名称=りんご, ランク=B, 値段=800
'名称=りんご, ランク=A, 値段=1,000


今まで使う必要に迫られたことはないのですが…
OrderByメソッドやThenByメソッドの第二引数にICompareを指定することで、独自の並び替え方法でソートすることができます。

例えば「2015/1/1, 2015/11/11, 2015/5/31, 2015/4, 20」といった日付を表す文字列があったとします。
これらを文字列としてソートすると「20, 2015/1/1, 2015/11/11, 2015/4, 2015/5/31」となります。
日付と考えるのであれば「20, 2015/1/1, 2015/4, 2015/5/31, 2015/11/11」と並んで欲しいです。
このようなとき独自のソート方法を定義します。

独自のソート方法を定義するには
IComparerインターフェイスを実装したクラスを作成し、Compareメソッドに比較方法を記述します。
【C#】
private class CompareDateString : System.Collections.Generic.IComparer<string>
{
    public int Compare(string x, string y)
    {
        //xがyより小さいときはマイナスの数、大きいときはプラスの数、同じときは0を返すようにする
        string[] aryX = x.Split('/');
        string[] aryY = y.Split('/');

        string xDateString;
        xDateString = aryX[0].PadLeft(4, '0');
        xDateString += (aryX.Length > 1 ? aryX[1].PadLeft(2, '0') : "00");
        xDateString += (aryX.Length > 2 ? aryX[2].PadLeft(2, '0') : "00");

        string yDateString;
        yDateString = aryY[0].PadLeft(4, '0');
        yDateString += (aryY.Length > 1 ? aryY[1].PadLeft(2, '0') : "00");
        yDateString += (aryY.Length > 2 ? aryY[2].PadLeft(2, '0') : "00");

        int i = xDateString.CompareTo(yDateString);
        return i;
    }
}
【VB】
    Private Class CompareDateString : Implements System.Collections.Generic.IComparer(Of String)

        Public Function Compare(x As String, y As String) As Integer _
            Implements System.Collections.Generic.IComparer(Of String).Compare
            
            'xがyより小さいときはマイナスの数、大きいときはプラスの数、同じときは0を返すようにする
            Dim aryX As String() = x.Split("/"c)
            Dim aryY As String() = y.Split("/"c)

            Dim xDateString As String
            xDateString = aryX(0).PadLeft(4, "0")
            xDateString += If(aryX.Length > 1, aryX(1).PadLeft(2, "0"), "00")
            xDateString += If(aryX.Length > 2, aryX(2).PadLeft(2, "0"), "00")

            Dim yDateString As String
            yDateString = aryY(0).PadLeft(4, "0")
            yDateString += If(aryY.Length > 1, aryY(1).PadLeft(2, "0"), "00")
            yDateString += If(aryY.Length > 2, aryY(2).PadLeft(2, "0"), "00")

            Dim i As Integer = xDateString.CompareTo(yDateString)
            Return i
        End Function

    End Class
OrderyByメソッドの第二引数にICompareを実装したCompareDateStringを指定します。
【C#】
string[] dateStrings = { "2015/1/1", "2015/11/11", "2015/5/31", "2015/4", "20" };

//普通に昇順でソート
var orderedList1 = dateStrings.OrderBy(str => str);
//出力
foreach (var itm in orderedList1)
    Console.WriteLine(itm);
//20
//2015/1/1
//2015/11/11
//2015/4
//2015/5/31

//第二引数にICompareを実装したCompareDateStringを指定しソート
var orderedList2 = dateStrings.OrderBy(str => str, new CompareDateString());
//出力
foreach (var itm in orderedList2)
    Console.WriteLine(itm);
//20
//2015/1/1
//2015/4
//2015/5/31
//2015/11/11
【VB】
Dim dateStrings As String() = {"2015/1/1", "2015/11/11", "2015/5/31", "2015/4", "20"}

'普通に昇順でソート
Dim orderedList1 = dateStrings.OrderBy(Function(str) str)

For Each itm In orderedList1
    Console.WriteLine(itm)
Next
'20
'2015/1/1
'2015/11/11
'2015/4
'2015/5/31

'第二引数にICompareを実装したCompareDateStringを指定しソート
Dim orderedList2 = dateStrings.OrderBy(Function(str) str, New CompareDateString())

For Each itm In orderedList2
    Console.WriteLine(itm)
Next
'20
'2015/1/1
'2015/4
'2015/5/31
'2015/11/11
※VBの場合、コンパイルオプションで「Option Infer」をONにし、型推論を有効にしてください。

.Net(VB C#) LINQのメソッド一覧

2015年12月2日水曜日

.Net(VB C#) LINQ
Enumerable.Where メソッド

Enumerable.Where メソッド
【C#】
述語に基づいて値のシーケンスをフィルター処理します。
public static IEnumerable<TSource> Where<TSource>
    (this IEnumerable<TSource> source, Func<TSource, bool> predicate)

述語に基づいて値のシーケンスをフィルター処理します。各要素のインデックスは、述語関数のロジックで使用されます。
public static IEnumerable<TSource> Where<TSource>
    (this IEnumerable source, Func predicate)
【VB】
述語に基づいて値のシーケンスをフィルター処理します。
<ExtensionAttribute>
Public Shared Function Where(Of TSource) 
    (source As IEnumerable(Of TSource), predicate As Func(Of TSource, Boolean)) 
    As IEnumerable(Of TSource)

述語に基づいて値のシーケンスをフィルター処理します。各要素のインデックスは、述語関数のロジックで使用されます。
<ExtensionAttribute>
Public Shared Function Where(Of TSource) 
    (source As IEnumerable(Of TSource), predicate As Func(Of TSource, Integer, Boolean)) 
    As IEnumerable(Of TSource)

簡単に言うと…
シーケンス(配列やコレクションなど)から抽出条件に合う要素を取り出す。

このメソッドは遅延実行されます。


テストデータです。
【C#】
private class Fruit
{
    public string Name { get; set; }
    public string Rank { get; set; }
    public decimal Price { get; set; }
}

var fruits = new List<Fruit>()
                {
                    new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 600 },
                    new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 },
                    new Fruit(){Name = "りんご", Rank = "B" , Price = 800 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 500 }
                };
【VB】
Private Class Fruit
    Public Property Name As String
    Public Property Rank As String
    Public Property Price As Decimal
End Class

Dim fruits = New List(Of Fruit)() From
            {
                New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600},
                New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200},
                New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500}
            }
果物リストから「ランクがAか値段が1000円以上」の果物を抽出します。

【C#】
var expensiveList = fruits.Where(itm => itm.Rank == "A" || itm.Price >= 1000);
            
//出力
foreach (var itm in expensiveList)
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));
//名称=りんご, ランク=A, 値段=1,000
//名称=みかん, ランク=A, 値段=600
//名称=ぶどう, ランク=B, 値段=1,200
//名称=みかん, ランク=A, 値段=500
【VB】
 Dim expensiveList = fruits.Where(Function(itm) itm.Rank = "A" OrElse itm.Price >= 1000)

'出力
For Each itm In expensiveList
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"))
Next
'名称=りんご, ランク=A, 値段=1,000
'名称=みかん, ランク=A, 値段=600
'名称=ぶどう, ランク=B, 値段=1,200
'名称=みかん, ランク=A, 値段=500

各要素のインデックスが取得できるWhereメソッドを使用して、
果物リストから偶数のインデックスの要素を抽出します。

【C#】
var evenList = fruits.Where((itm, idx) => (idx % 2) == 0);

//出力
foreach (var itm in evenList)
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));
//名称=りんご, ランク=A, 値段=1,000
//名称=ぶどう, ランク=B, 値段=1,200
//名称=みかん, ランク=A, 値段=500 
【VB】
Dim evenList = fruits.Where(Function(itm, idx) idx Mod 2 = 0)

'出力
For Each itm In evenList
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"))
Next
'名称=りんご, ランク=A, 値段=1,000
'名称=ぶどう, ランク=B, 値段=1,200
'名称=みかん, ランク=A, 値段=500
※VBの場合、コンパイルオプションで「Option Infer」をONにし、型推論を有効にしてください。

画面の検索条件などによりWhereメソッドの条件を動的に組み立てる事があると思います。
And検索であれば特に問題ないと思いますが、OR検索はWhereメソッドを単純に使用するだけでは実現できません。
いろいろ方法はあると思いますが、Whereメソッドを使用した簡単なOr検索を実現する方法をコチラに書いてみました。
.Net(VB C#) LINQ Enumerable.Where メソッドのAND条件やOR条件を動的に組み立てる

.Net(VB C#) LINQのメソッド一覧

.Net(VB C#) LINQ
Enumerable.Where メソッドのAND条件やOR条件を動的に組み立てる

画面の検索条件などによりWhereメソッドの条件を動的に組み立てる事があると思います。
「Linq 動的 Where」などってググってみると、いろいろ情報が出てくると思います。
Expressionで動的に組み立てるのが正攻法なようですね。
う~んメンドクサイ。

メンドクサイ事はイヤなので簡単に動的OR検索する方法です。

テストデータです。
【C#】
private class Fruit
{
    public string Name { get; set; }
    public string Rank { get; set; }
    public decimal Price { get; set; }
}

var fruits = new List<Fruit>()
                {
                    new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 600 },
                    new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 },
                    new Fruit(){Name = "りんご", Rank = "B" , Price = 800 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 500 },
                    new Fruit(){Name = "ばなな", Rank = "C" , Price = 100 }
                };
【VB】
Private Class Fruit
    Public Property Name As String
    Public Property Rank As String
    Public Property Price As Decimal
End Class

Dim fruits = New List(Of Fruit)() From
            {
                New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600},
                New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200},
                New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500},
                New Fruit() With {.Name = "ばなな", .Rank = "C", .Price = 500}
            }

検索画面から検索条件が指定されたとします。

名称は複数指定でき、条件値はリストになっています。今回の条件は「りんご または みかん」
ランクも複数指定で、条件値はリストになっています。今回の条件は指定がありません。
値段は下限値と上限値が指定できます。今回の条件は800円以下です。
つまり、今回の条件は「りんご か みかん」 か 「800円以下」の果物になります。

【C#】
//検索条件
//--名称
var searchNameList = new List<string>() { "りんご", "みかん" };
//--ランク
var searchRankList = new List<string>() { /*指定なし*/ };
//--値段(下限、上限)
var priceRange = new Tuple<decimal?, decimal?>(null, 800);

【VB】
'検索条件
'--名称
Dim searchNameList = New List(Of String)() From {"りんご", "みかん"}
'--ランク
Dim searchRankList = New List(Of String)() From {} '指定なし
'--値段(下限、上限)
Dim priceRange = New Tuple(Of Decimal?, Decimal?)(Nothing, 800)

次に検索条件が指定されている場合に実行する処理を、リストに溜めて行きます。

List型の変数「funcSearchList」には、Fruit型の引数をとりboolを返す処理を格納できます。
例えば名称で検索する場合、Fruit型の引数が名称検索条件内に存在すればtrueを返す処理を、リストに追加しています。

【C#】
//検索処理リストに検索条件ごとの処理を追加していく
var funcSeachList = new List<Func<Fruit, bool>>();

//--名称
if (searchNameList.Count > 0)
{
    funcSeachList.Add((fruit) => { return searchNameList.Contains(fruit.Name); });
}
//--ランク
if (searchRankList.Count > 0)
{
    funcSeachList.Add((fruit) => { return searchRankList.Contains(fruit.Rank); });
}
//--値段
if (priceRange.Item1.HasValue)
{
    funcSeachList.Add((fruit) => { return fruit.Price >= priceRange.Item1.Value; });
}
if (priceRange.Item2.HasValue)
{
    funcSeachList.Add((fruit) => { return fruit.Price <= priceRange.Item2.Value; });
}
【VB】
'検索処理リストに検索条件ごとの処理を追加していく
Dim funcSeachList = New List(Of Func(Of Fruit, Boolean))()
'--名称
If (searchNameList.Count > 0) Then
    funcSeachList.Add(Function(fruit) searchNameList.Contains(fruit.Name))
End If
'--ランク
If (searchRankList.Count > 0) Then
    funcSeachList.Add(Function(fruit) searchRankList.Contains(fruit.Rank))
End If
'--値段
If (priceRange.Item1.HasValue) Then
    funcSeachList.Add(Function(fruit) fruit.Price >= priceRange.Item1.Value)
End If
If (priceRange.Item2.HasValue) Then
    funcSeachList.Add(Function(fruit) fruit.Price <= priceRange.Item2.Value)
End If

検索処理を溜めたList型の変数「funcSearchList」から、OR検索用のメソッドを作成します。

【C#】
//--Or検索用メソッドの作成
Func<Fruit, bool> searchOr = (fruit) =>
{
    //--検索条件が1件もなければ、要素は条件に一致したとする。
    if (funcSeachList.Count == 0)
        return true;
    //--要素が検索条件に一致するか判定する
    bool? result = null;
    foreach (var func in funcSeachList)
    {
        bool funcResult = func(fruit);
        //And検索したければココを||から&&に変更する
        result = (result.HasValue ? result.Value || funcResult : funcResult);     
    }
    return result.Value;
};
【VB】
'--Or検索用メソッドの作成
Dim searchOr = Function(fruit As Fruit)
                   '--検索条件が1件もなければ、要素は条件に一致したとする。
                   If (funcSeachList.Count = 0) Then
                       Return True
                   End If
                   '--要素が検索条件に一致するか判定する
                   Dim result As Nullable(Of Boolean) = Nothing
                   For Each func In funcSeachList
                       Dim funcResult As Boolean = func(fruit)
                       'And検索したければココをOrElseからAndAlsoに変更する
                       result = If(result.HasValue, result.Value OrElse funcResult, funcResult) 
                   Next
                   Return result.Value
               End Function
あとは作成したメソッドをWhereメソッドの引数に指定してあげればOKです。

【C#】
//Or検索
var searchOrResultList = fruits.Where(itm => { return searchOr(itm); });

//出力
foreach (var itm in searchOrResultList)
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));
//名称=りんご, ランク=A, 値段=1,000
//名称=みかん, ランク=A, 値段=600
//名称=りんご, ランク=B, 値段=800
//名称=みかん, ランク=A, 値段=500
//名称=ばなな, ランク=C, 値段=100
【VB】
'Or検索
Dim searchOrResultList = fruits.Where(Function(itm) searchOr(itm))

'出力
For Each itm In searchOrResultList
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"))
Next
'名称=りんご, ランク=A, 値段=1,000
'名称=みかん, ランク=A, 値段=600
'名称=りんご, ランク=B, 値段=800
'名称=みかん, ランク=A, 値段=500
'名称=ばなな, ランク=C, 値段=100

コード全体です。

【C#】
//テストデータ作成
var fruits = new List<Fruit>()
    {
        new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 },
        new Fruit(){Name = "みかん", Rank = "A" , Price = 600 },
        new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 },
        new Fruit(){Name = "りんご", Rank = "B" , Price = 800 },
        new Fruit(){Name = "みかん", Rank = "A" , Price = 500 },
        new Fruit(){Name = "ばなな", Rank = "C" , Price = 100 }
    };

/* 検索画面から検索条件が指定されたとする */
//検索条件
//--名称
var searchNameList = new List<string>() { "りんご", "みかん" };
//--ランク
var searchRankList = new List<string>() { /*指定なし*/ };
//--値段(下限、上限)
var priceRange = new Tuple<decimal?, decimal?>(null, 800);


//検索処理リストに検索条件ごとの処理を追加していく
var funcSeachList = new List<Func<Fruit, bool>>();
//--名称
if (searchNameList.Count > 0)
{
    funcSeachList.Add((fruit) => { return searchNameList.Contains(fruit.Name); });
}
//--ランク
if (searchRankList.Count > 0)
{
    funcSeachList.Add((fruit) => { return searchRankList.Contains(fruit.Rank); });
}
//--値段
if (priceRange.Item1.HasValue)
{
    funcSeachList.Add((fruit) => { return fruit.Price >= priceRange.Item1.Value; });
}
if (priceRange.Item2.HasValue)
{
    funcSeachList.Add((fruit) => { return fruit.Price <= priceRange.Item2.Value; });
}

//Or検索用メソッドの作成
Func<Fruit, bool> searchOr = (fruit) =>
{
    //--検索条件が1件もなければ、要素は条件に一致したとする。
    if (funcSeachList.Count == 0)
        return true;
    //--要素が検索条件に一致するか判定する
    bool? result = null;
    foreach (var func in funcSeachList)
    {
        bool funcResult = func(fruit);
        result = (result.HasValue ? result.Value || funcResult : funcResult);
    }
    return result.Value;
};

//Or検索
var searchOrResultList = fruits.Where(itm => { return searchOr(itm); });

//出力
foreach (var itm in searchOrResultList)
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));
【VB】
'テストデータ作成
Dim fruits = New List(Of Fruit)() From
    {
        New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000},
        New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600},
        New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200},
        New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800},
        New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500},
        New Fruit() With {.Name = "ばなな", .Rank = "C", .Price = 500}
    }

'検索画面から検索条件が指定されたとする
'検索条件
'--名称
Dim searchNameList = New List(Of String)() From {"りんご", "みかん"}
'--ランク
Dim searchRankList = New List(Of String)() From {} '指定なし
'--値段(下限、上限)
Dim priceRange = New Tuple(Of Decimal?, Decimal?)(Nothing, 800)


'検索処理リストに検索条件ごとの処理を追加していく
Dim funcSeachList = New List(Of Func(Of Fruit, Boolean))()
'--名称
If (searchNameList.Count > 0) Then
    funcSeachList.Add(Function(fruit) searchNameList.Contains(fruit.Name))
End If
'--ランク
If (searchRankList.Count > 0) Then
    funcSeachList.Add(Function(fruit) searchRankList.Contains(fruit.Rank))
End If
'--値段
If (priceRange.Item1.HasValue) Then
    funcSeachList.Add(Function(fruit) fruit.Price >= priceRange.Item1.Value)
End If
If (priceRange.Item2.HasValue) Then
    funcSeachList.Add(Function(fruit) fruit.Price <= priceRange.Item2.Value)
End If

'Or検索用メソッドの作成
Dim searchOr = Function(fruit As Fruit)
                   '--検索条件が1件もなければ、要素は条件に一致したとする。
                   If (funcSeachList.Count = 0) Then
                       Return True
                   End If
                   '--要素が検索条件に一致するか判定する
                   Dim result As Nullable(Of Boolean) = Nothing
                   For Each func In funcSeachList
                       Dim funcResult As Boolean = func(fruit)
                       result = If(result.HasValue, result.Value OrElse funcResult, funcResult)
                   Next
                   Return result.Value
               End Function

'Or検索
Dim searchOrResultList = fruits.Where(Function(itm) searchOr(itm))

'出力
For Each itm In searchOrResultList
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"))
Next

Or検索用メソッドsearchOrのOR演算子「|| または OrElse」をAND演算子「&& または AndAlso」にかえればAND検索になります。
検索処理を溜めたList型の変数「funcSearchList」に、検索メソッドと一緒にAND検索かOR検索か判定するフラグも格納すればAND検索とOR検索のゴチャ混ぜ検索もできます。

OR検索用メソッドを組み立てる部分は汎用化できるので、Linqに独自の拡張メソッドとして追加してもいいかと思います。
以下はOR検索する処理のリストを引数にとり、条件に該当した要素のリストを返す拡張メソッドです。

【C#】
public static class LinqExtension
{
    public static IEnumerable<T> WhereOr<T>(this IEnumerable<T> source, List<Func<T, bool>> lstFunc)
    {
        //検索条件が1件もなければ、要素は条件に一致したとする。
        if (lstFunc.Count == 0)
            return source;
        //検索条件に一致した要素のリストを作成する
        var returnList = new List<T>();
        foreach (var item in source)
        {
            //--要素が検索条件に一致するか判定する
            bool? match = null;
            foreach (var func in lstFunc)
            {
                bool funcResult = func(item);
                match = (match.HasValue ? match.Value || funcResult : funcResult);
            }
            //--条件が一致した要素のみリストに追加
            if (match.Value == true)
                returnList.Add(item);
        }
        return returnList;
    }
}
【VB】
Imports System.Runtime.CompilerServices

Module LinqExtenstions

    <Extension()> _
    Public Function WhereOr(Of T) _
        (ByVal source As IEnumerable(Of T), lstFunc As List(Of Func(Of T, Boolean))) As IEnumerable(Of T)
        '検索条件が1件もなければ、要素は条件に一致したとする。
        If (lstFunc.Count = 0) Then
            Return source
        End If
        '検索条件に一致した要素のリストを作成する
        Dim returnList = New List(Of T)()
        For Each item In source
            '--要素が検索条件に一致するか判定する
            Dim match As Nullable(Of Boolean) = Nothing
            For Each func In lstFunc
                Dim funcResult As Boolean = func(item)
                match = If(match.HasValue, match.Value OrElse funcResult, funcResult)
            Next
            '--条件が一致した要素のみリストに追加
            If (match.Value = True) Then
                returnList.Add(item)
            End If
        Next
        Return returnList
    End Function

End Module

使用方法は、先ほどのWhereメソッド部分をWhereOrメソッドに変更します。
【C#】
//テストデータ作成
var fruits = new List<Fruit>()
    {
        new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 },
        new Fruit(){Name = "みかん", Rank = "A" , Price = 600 },
        new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 },
        new Fruit(){Name = "りんご", Rank = "B" , Price = 800 },
        new Fruit(){Name = "みかん", Rank = "A" , Price = 500 },
        new Fruit(){Name = "ばなな", Rank = "C" , Price = 100 }
    };

/* 検索画面から検索条件が指定されたとする */
//検索条件
//--名称
var searchNameList = new List<string>() { "りんご", "みかん" };
//--ランク
var searchRankList = new List<string>() { /*指定なし*/ };
//--値段(下限、上限)
var priceRange = new Tuple<decimal?, decimal?>(null, 800);


//検索処理リストに検索条件ごとの処理を追加していく
var funcSeachList = new List<Func<Fruit, bool>>();
//--名称
if (searchNameList.Count > 0)
{
    funcSeachList.Add((fruit) => { return searchNameList.Contains(fruit.Name); });
}
//--ランク
if (searchRankList.Count > 0)
{
    funcSeachList.Add((fruit) => { return searchRankList.Contains(fruit.Rank); });
}
//--値段
if (priceRange.Item1.HasValue)
{
    funcSeachList.Add((fruit) => { return fruit.Price >= priceRange.Item1.Value; });
}
if (priceRange.Item2.HasValue)
{
    funcSeachList.Add((fruit) => { return fruit.Price <= priceRange.Item2.Value; });
}


//Or検索
//独自に追加したWhereOrメソッドを使用する
var searchOrResultList = fruits.WhereOr(funcSeachList);

//出力
foreach (var itm in searchOrResultList)
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));
【VB】
'テストデータ作成
Dim fruits = New List(Of Fruit)() From
    {
        New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000},
        New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600},
        New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200},
        New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800},
        New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500},
        New Fruit() With {.Name = "ばなな", .Rank = "C", .Price = 500}
    }

'検索画面から検索条件が指定されたとする
'検索条件
'--名称
Dim searchNameList = New List(Of String)() From {"りんご", "みかん"}
'--ランク
Dim searchRankList = New List(Of String)() From {} '指定なし
'--値段(下限、上限)
Dim priceRange = New Tuple(Of Decimal?, Decimal?)(Nothing, 800)


'検索処理リストに検索条件ごとの処理を追加していく
Dim funcSeachList = New List(Of Func(Of Fruit, Boolean))()
'--名称
If (searchNameList.Count > 0) Then
    funcSeachList.Add(Function(fruit) searchNameList.Contains(fruit.Name))
End If
'--ランク
If (searchRankList.Count > 0) Then
    funcSeachList.Add(Function(fruit) searchRankList.Contains(fruit.Rank))
End If
'--値段
If (priceRange.Item1.HasValue) Then
    funcSeachList.Add(Function(fruit) fruit.Price >= priceRange.Item1.Value)
End If
If (priceRange.Item2.HasValue) Then
    funcSeachList.Add(Function(fruit) fruit.Price <= priceRange.Item2.Value)
End If

'Or検索
'独自に追加したWhereOrメソッドを使用する
Dim searchOrResultList = fruits.WhereOr(funcSeachList)

'出力
For Each itm In searchOrResultList
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"))
Next


.Net(VB C#) LINQのメソッド一覧

2015年12月1日火曜日

.Net(VB C#) LINQ
Enumerable.Select メソッド

Enumerable.Select メソッド
【C#】
シーケンスの各要素を新しいフォームに射影します。
public static IEnumerable<TResult> Select<TSource, TResult>
    (this IEnumerable<TSource> source, Func<TSource, TResult> selector)

要素のインデックスを組み込むことにより、シーケンスの各要素を新しいフォームに射影します。
public static IEnumerable<TResult> Select<TSource, TResult>
    (this IEnumerable source, Func selector)
【VB】
シーケンスの各要素を新しいフォームに射影します。
<ExtensionAttribute>
Public Shared Function Select(Of TSource, TResult) 
    (source As IEnumerable(Of TSource), selector As Func(Of TSource, TResult)) 
    As IEnumerable(Of TResult)

要素のインデックスを組み込むことにより、シーケンスの各要素を新しいフォームに射影します。
<ExtensionAttribute>
Public Shared Function Select(Of TSource, TResult) 
    (source As IEnumerable(Of TSource), selector As Func(Of TSource, Integer, TResult)) 
    As IEnumerable(Of TResult)

簡単に言うと…
シーケンス(配列やコレクションなど)の要素から特定のプロパティを取り出し、新しい要素のシーケンスを作成する。

このメソッドは遅延実行されます。


テストデータです。
【C#】
private class Fruit
{
    public string Name { get; set; }
    public string Rank { get; set; }
    public decimal Price { get; set; }
}

var fruits = new List<Fruit>()
                {
                    new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 600 },
                    new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 },
                    new Fruit(){Name = "りんご", Rank = "B" , Price = 800 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 500 }
                };
【VB】
Private Class Fruit
    Public Property Name As String
    Public Property Rank As String
    Public Property Price As Decimal
End Class

Dim fruits = New List(Of Fruit)() From
            {
                New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600},
                New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200},
                New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500}
            }


まず果物リストの各果物要素の名前だけを取り出します。
【C#】
var nameList = fruits.Select(itm => itm.Name);

//出力
foreach (var itm in nameList)
    Console.WriteLine("名称={0}", itm);  
//名称=りんご
//名称=みかん
//名称=ぶどう
//名称=りんご
//名称=みかん 
【VB】
Dim nameList = fruits.Select(Function(itm) itm.Name)

'出力
For Each itm In nameList
    Console.WriteLine("名称={0}", itm)
Next
'名称=りんご
'名称=みかん
'名称=ぶどう
'名称=りんご
'名称=みかん

次に名前と値段(税込)を取り出してみます。
複数のプロパティを取り出す場合、匿名クラスを利用します。
※匿名クラスでなく、普通のクラスも利用できます。
【C#】
var lst = fruits.Select(itm => new { itm.Name, ZeikomiPrice = itm.Price * 1.08m });

//出力
foreach (var itm in lst)
    Console.WriteLine("名称={0}, 値段(税込)={1}", itm.Name, itm.ZeikomiPrice.ToString("#,##0"));  
//名称=りんご, 値段(税込)=1,080
//名称=みかん, 値段(税込)=648
//名称=ぶどう, 値段(税込)=1,296
//名称=りんご, 値段(税込)=864
//名称=みかん, 値段(税込)=540  
【VB】
Dim lst = fruits.Select(Function(itm) New With {itm.Name, .ZeikomiPrice = itm.Price * 1.08D})

'出力
For Each itm In lst
    Console.WriteLine("名称={0}, 値段(税込)={1}", itm.Name, itm.ZeikomiPrice.ToString("#,##0"))
Next
'名称=りんご, 値段(税込)=1,080
'名称=みかん, 値段(税込)=648
'名称=ぶどう, 値段(税込)=1,296
'名称=りんご, 値段(税込)=864
'名称=みかん, 値段(税込)=540   

匿名クラスを利用せずに、普通のクラスを利用して、値段を2倍した果物リストを作成します。
【C#】
var lst = fruits.Select(itm => new Fruit(){Name = "ボッタクリ" + itm.Name, Rank = itm.Rank, Price = itm.Price * 2});

//出力
foreach (var itm in lst)
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));
//名称=ボッタクリりんご, ランク=A, 値段=2,000
//名称=ボッタクリみかん, ランク=A, 値段=1,200
//名称=ボッタクリぶどう, ランク=B, 値段=2,400
//名称=ボッタクリりんご, ランク=B, 値段=1,600
//名称=ボッタクリみかん, ランク=A, 値段=1,000 
【VB】
Dim lst = fruits.Select(Function(itm) _
   New Fruit() With {.Name = "ボッタクリ" + itm.Name, .Rank = itm.Rank, .Price = itm.Price * 2})

'出力
For Each itm In lst
 Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"))
Next
'名称=ボッタクリりんご, ランク=A, 値段=2,000
'名称=ボッタクリみかん, ランク=A, 値段=1,200
'名称=ボッタクリぶどう, ランク=B, 値段=2,400
'名称=ボッタクリりんご, ランク=B, 値段=1,600
'名称=ボッタクリみかん, ランク=A, 値段=1,000 

Selectメソッドは各要素のインデックスが取得できるオーバーロードがあります。
各要素のインデックスが取得できるSelectメソッドを使用して、果物リストからインデックスを取り出します。
【C#】
var lst = fruits.Select((itm, idx) => new { Index = idx, Name = itm.Name});

//出力
foreach (var itm in lst)
    Console.WriteLine("インデックス={0}, 名称={1}", itm.Index, itm.Name);
//インデックス=0, 名称=りんご
//インデックス=1, 名称=みかん
//インデックス=2, 名称=ぶどう
//インデックス=3, 名称=りんご
//インデックス=4, 名称=みかん
【VB】
Dim lst = fruits.Select(Function(itm, idx) New With {.Index = idx, itm.Name})

'出力
For Each itm In lst
    Console.WriteLine("インデックス={0}, 名称={1}", itm.Index, itm.Name)
Next
'インデックス=0, 名称=りんご
'インデックス=1, 名称=みかん
'インデックス=2, 名称=ぶどう
'インデックス=3, 名称=りんご
'インデックス=4, 名称=みかん
※VBの場合、コンパイルオプションで「Option Infer」をONにし、型推論を有効にしてください。

.Net(VB C#) LINQのメソッド一覧