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のメソッド一覧

2015年11月30日月曜日

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

LINQのメソッド一覧です。

メソッド名実行方法機能サンプル
Select遅延実行シーケンスの各要素を新しいフォームに射影します。サンプルコード
Where遅延実行述語に基づいて値のシーケンスをフィルター処理します。サンプルコード
OrderBy遅延実行シーケンスの要素を昇順に並べ替えます。サンプルコード
ThenBy遅延実行シーケンス内の後続の要素を昇順で配置します。
OrderByDescending遅延実行シーケンスの要素を降順に並べ替えます。サンプルコード
ThenByDescending 遅延実行シーケンス内の後続の要素を降順で配置します。
Cast遅延実行IEnumerable の要素を、指定した型にキャストします。サンプルコード
OfType遅延実行指定された型に基づいて IEnumerable の要素をフィルター処理します。
ElementAt即時実行シーケンス内の指定されたインデックス位置にある要素を返します。サンプルコード
ElementAtOrDefault即時実行シーケンス内の指定されたインデックス位置にある要素を返します。インデックスが範囲外の場合は既定値を返します。
First即時実行シーケンスの最初の要素を返します。サンプルコード
FirstOrDefault即時実行シーケンスの最初の要素を返します。要素が見つからない場合は既定値を返します。
Last即時実行シーケンスの最後の要素を返します。サンプルコード
LastOrDefault即時実行シーケンスの最後の要素を返します。要素が見つからない場合は既定値を返します。
Single即時実行シーケンスの 1 つの特定の要素を返します。サンプルコード
SingleOrDefault 即時実行シーケンスの 1 つの特定の要素を返します。そのような要素が見つからない場合は既定値を返します。
Take遅延実行シーケンスの先頭から、指定された数の連続する要素を返します。サンプルコード
TakeWhile遅延実行指定された条件を満たされる限り、シーケンスから要素を返した後、残りの要素をスキップします。サンプルコード
Skip遅延実行シーケンス内の指定された数の要素をバイパスし、残りの要素を返します。
SkipWhile遅延実行指定された条件が満たされる限り、シーケンスの要素をバイパスした後、残りの要素を返します。
Contains即時実行指定した要素がシーケンスに格納されているかどうかを判断します。
All即時実行シーケンスのすべての要素が条件を満たしているかどうかを判断します。
Any即時実行シーケンスに要素が含まれているかどうかを判断します。
SequenceEqual即時実行要素の型に対して既定の等値比較子を使用して要素を比較することで、2 つのシーケンスが等しいかどうかを判断します。
Reverse遅延実行シーケンスの要素の順序を反転させます。
Repeat遅延実行繰り返される 1 つの値を含むシーケンスを生成します。
GroupBy遅延実行シーケンスの要素をグループ化します。
Distinct遅延実行シーケンスから一意の要素を返します。
Concat遅延実行2 つのシーケンスを連結します。
SelectMany遅延実行シーケンスの各要素を IEnumerable(Of T) に射影し、結果のシーケンスを 1 つのシーケンスに平坦化します。
Zip遅延実行2 つのシーケンスの対応する要素に対して、1 つの指定した関数を適用し、結果として 1 つのシーケンスを生成します。
Range遅延実行指定した範囲内の整数のシーケンスを生成します。
Count即時実行シーケンス内の要素数を返します。
LongCount即時実行シーケンス内の要素数を表す Int64 を返します。
Max即時実行値のシーケンスの最大値を返します。
Min即時実行値のシーケンスの最小値を返します。
Average即時実行数値のシーケンスの平均値を計算します。
Sum 即時実行数値のシーケンスの合計を計算します。
ToArray即時実行IEnumerable から配列を作成します。
ToDictionary即時実行IEnumerable から Dictionary を作成します。
ToList即時実行IEnumerable から List を作成します。
ToLookup即時実行IEnumerable からジェネリックの Lookup を作成します。
Join遅延実行一致するキーに基づいて 2 つのシーケンスの要素を相互に関連付けます。
GroupJoin遅延実行キーが等しいかどうかに基づいて 2 つのシーケンスの要素を相互に関連付け、その結果をグループ化します。
Intersect遅延実行2 つのシーケンスの積集合を生成します。
Union遅延実行2 つのシーケンスの和集合を生成します。
Except 遅延実行2 つのシーケンスの差集合を生成します。
Empty即時実行指定した型引数を持つ空の IEnumerable を返します。
DefaultIfEmpty即時実行IEnumerable の要素を返します。シーケンスが空の場合は既定値を持つシングルトン コレクションを返します。
AsEnumerable即時実行IEnumerable として型指定された入力を返します。

2015年11月29日日曜日

PLSQL 制御文 ~GOTO文とラベル、NULL文~

PLSQLではGOTO文が使用できます。
しかし、他の言語同様に多用するのは厳禁です。

11g以前ではループのスキップにcontinue文が使用できないので、GOTO文を使用する機会があるかと思います。

GOTO文とラベル、NULL文の基本的な使い方


まずはGOTO文とラベルの使い方です。
ラベルは「「<<ラベル名>>」と定義します。
GOTO文で「GOTO ラベル名;」とすることで、指定したラベルに制御を移すことができます。
処理がないラベルを定義する事はできません。
ラベルの処理が何もない場合はNULL文を使用します。 NULL文は何もしないステートメントを意味します。
DECLARE
    num NUMBER := &任意の数値;
BEGIN
    
    IF (num < 10) THEN
        GOTO under_ten;
    ELSE
        GOTO over_ten;
    END IF;
    
    <<under_ten>>
        SYS.DBMS_OUTPUT.PUT_LINE('10以下');
    
    <<over_ten>>
        NULL;
END;

ループのContinue文の代わりにGOTO文を使用する(11g以前)


11g以前ではContinue文が使用できないので、ループのスキップにGOTO文を使用することがあります。
※11g以降はcontinue文を使用したほうがよいと思います。
Continue文についてはコチラ 「PLSQL 制御文 ~LOOP文~

以下はGOTO文を使用した、ループのスキップです。
ループカウンタが偶数の場合は、ループ処理をスキップします。
DECLARE
    
BEGIN

    FOR num IN 1..10 LOOP
        IF (num Mod 2 = 0) THEN
            GOTO continue;
        END IF;
        
        SYS.DBMS_OUTPUT.PUT_LINE(num);
        
        <<continue>>
            NULL;
    END LOOP;

END;

ループラベル


ループ開始の直前にラベルを付けるとそのラベルは「ループラベル」になります。
11g以降ではContinue文が使用できますが、「Continue ラベル;」や「Continue ラベル WHEN 条件;」と書くことで、スキップするループを指定することができます。
同様にEXIT文についても「Exit ラベル;」や「Exit ラベル WHEN 条件;」と書くことで、終了するループを指定することができます。
ループをネストした多重ループで、スキップしたいループや終了したいループを指定するときに使用します。
DECLARE
 
BEGIN

    <<loop_1>>
    FOR i IN 1..3 LOOP
         
        <<loop_2>>
        FOR j IN 1..3 LOOP
            
            <<loop_3>>       
            FOR k iN 1..3 LOOP
                dbms_output.put_line('i=' || i || ', j=' || j || ', k=' || k );
                
                --j=2はループ2をスキップ
                CONTINUE loop_2 WHEN ( j = 2);
                --j=3はループ1をスキップ
                IF (j = 3) THEN
                    CONTINUE loop_1;
                END IF;
                
                
                --i=2はループ3を終了
                EXIT loop_3 WHEN ( i = 3);
                --i=3はループ2を終了
                IF (i = 3) THEN
                    EXIT loop_2;
                END IF;
               
            
            END LOOP; --loop1
            
        END LOOP; --loop2
    
    END LOOP; --loop3

   dbms_output.put_line('ループ終了');
END;
出力結果
i=1, j=1, k=1
i=1, j=1, k=2
i=1, j=1, k=3
i=1, j=2, k=1
i=1, j=3, k=1
i=2, j=1, k=1
i=2, j=1, k=2
i=2, j=1, k=3
i=2, j=2, k=1
i=2, j=3, k=1
i=3, j=1, k=1
i=3, j=2, k=1
i=3, j=3, k=1
ループ終了

2015年11月26日木曜日

PLSQL 暗黙カーソル

前回までのカーソルの使い方についてはコチラ
PLSQL SELECTの結果を取得する~取得結果が複数行の場合~
PLSQL カーソルを使用してデータを取得する
PLSQL パラメータ付きカーソル
PLSQL カーソル属性

カーソルには「明示カーソル」と「暗黙カーソル」の2種類があります。

明示カーソル


カーソルに名前をつけているものを明示カーソルと呼びます。
前回までのカーソルを使用したサンプルでは、以下のサンプルのようにDECLARE部でカーソルに対して名前を付けています。
DECLARE
    --カーソル定義
    CURSOR member_csr IS 
        SELECT * FROM Member;
        
    …以下略…

暗黙カーソル


明示カーソル以外で埋め込まれたSQLが実行されるとき、Oracleによって暗黙的にカーソルが作成され実行されます。

次のコードは暗黙カーソルを使用したSELECT文、UPDATE文です。
DECLARE
    membername Member.MemberName%Type;
    
BEGIN
    
    --暗黙カーソルを使用したSELET文
    SELECT MemberName Rank INTO membername
        FROM Member 
        WHERE MemberId = '1';
        
    --暗黙カーソルを使用したSELET文のLOOP
    FOR rec IN (SELECT * FROM Member) LOOP
        SYS.DBMS_OUTPUT.PUT_LINE(rec.MemberId || ' ' || rec.MemberName);
    END LOOP;
    
    --暗黙カーソルを使用したUPDATE文
    UPDATE Member 
        SET MemberName = 'ishida', Rank = 'C'
        WHERE MemberId = '1';

END;
この暗黙カーソルにも明示カーソルと同じく属性があります。
暗黙カーソルのカーソル名は「SQL」になり、属性値を調べるには「SQL%属性名」とします。
但しカーソル名は「SQL」しかないため、直前に実行したSQLの属性しか調べることができません。
属性意味
FOUNDカーソルを実行した結果、該当するものがあったかどうか
NOTFOUNDFOUNDの逆値
ISOPENカーソルが開いているかどうか
暗黙カーソルでは常にfalse
ROWCOUNTカーソルを処理した結果、処理された行数
SELECTの場合フェッチした件数

下記のコードでは暗黙カーソルの属性を調べています。
DECLARE
    membername Member.MemberName%Type;
    
BEGIN 
          
    -- ■ 暗黙カーソルを使用したSELET文のカーソルFORループ ■ --
    --出力結果
    --ISOPEN=false
    --FOUND=false
    --ROWCOUNT=
    
    FOR rec IN (SELECT * FROM Member WHERE MemberId = '1') LOOP
    
        --ISOPEN属性の確認
        IF (SQL%ISOPEN) THEN
            SYS.DBMS_OUTPUT.PUT_LINE('ISOPEN=true');
        ELSE
            SYS.DBMS_OUTPUT.PUT_LINE('ISOPEN=false');
        END IF;
      
        --FOUND属性の確認
        IF (SQL%FOUND) THEN
            SYS.DBMS_OUTPUT.PUT_LINE('FOUND=true');
        ELSE
            SYS.DBMS_OUTPUT.PUT_LINE('FOUND=false');
        END IF;
      
        --ROWCOUNT属性の確認
        SYS.DBMS_OUTPUT.PUT_LINE('ROWCOUNT=' || SQL%ROWCOUNT);
        
    END LOOP;
    
    -- ■ 暗黙カーソルを使用したSELET文 ■ --
    --出力結果
    --ISOPEN=false
    --FOUND=true
    --ROWCOUNT=1
    
    SELECT MemberName INTO membername
        FROM Member 
        WHERE MemberId = '1';
        
    --ISOPEN属性の確認
    IF (SQL%ISOPEN) THEN
        SYS.DBMS_OUTPUT.PUT_LINE('ISOPEN=true');
    ELSE
        SYS.DBMS_OUTPUT.PUT_LINE('ISOPEN=false');
    END IF;
    
    --FOUND属性の確認
    IF (SQL%FOUND) THEN
        SYS.DBMS_OUTPUT.PUT_LINE('FOUND=true');
    ELSE
        SYS.DBMS_OUTPUT.PUT_LINE('FOUND=false');
    END IF;
    
    --ROWCOUNT属性の確認
    SYS.DBMS_OUTPUT.PUT_LINE('ROWCOUNT=' || SQL%ROWCOUNT);
    
    -- ■ 暗黙カーソルを使用したUPDATE文 ■ --
    --出力結果
    --ISOPEN=false
    --FOUND=true
    --ROWCOUNT=2
    
    UPDATE Member 
        SET MemberName = 'ishida', Rank = 'C'
        WHERE MemberId > '1';
 
     --ISOPEN属性の確認
    IF (SQL%ISOPEN) THEN
        SYS.DBMS_OUTPUT.PUT_LINE('ISOPEN=true');
    ELSE
        SYS.DBMS_OUTPUT.PUT_LINE('ISOPEN=false');
    END IF;
    
    --FOUND属性の確認
    IF (SQL%FOUND) THEN
        SYS.DBMS_OUTPUT.PUT_LINE('FOUND=true');
    ELSE
        SYS.DBMS_OUTPUT.PUT_LINE('FOUND=false');
    END IF;
    
    --ROWCOUNT属性の確認
    SYS.DBMS_OUTPUT.PUT_LINE('ROWCOUNT=' || SQL%ROWCOUNT);       
END;
暗黙カーソルを使用したカーソルFORループでは属性が何も取れないのですね。
明示カーソルを使用したカーソルFORループでの各属性は、コチラ「PLSQL カーソル属性 」に書いてます。

PLSQL カーソル属性

前回までのカーソルの使い方についてはコチラ
PLSQL SELECTの結果を取得する~取得結果が複数行の場合~
PLSQL カーソルを使用してデータを取得する
PLSQL パラメータ付きカーソル

今回はカーソルの属性についてです。

カーソルには下記の表のような属性があります。
「カーソル名%属性名」とすることで、属性の値をを取得できます。
属性意味
FOUNDカーソルを実行した結果、該当するものがあったかどうか
NOTFOUNDFOUNDの逆値
ISOPENカーソルが開いているかどうか
ROWCOUNTカーソルを処理した結果、処理された行数
SELECTの場合フェッチした件数

カーソル属性を確認するために、複数件の結果を返すSELECT文で各属性を出力するコードを実行してみました。

DECLARE
   CURSOR cur IS 
       SELECT * FROM Member;
       
   rec cur%Rowtype;
BEGIN
    --カーソル状態出力
    If (cur%ISOPEN) THEN
        SYS.DBMS_OUTPUT.PUT_LINE('カーソルオープン前:ISOPEN=true');
    ELSE
         SYS.DBMS_OUTPUT.PUT_LINE('カーソルオープン前:ISOPEN=false');   
    END IF;
    
    --カーソルオープン
    OPEN cur;
    
    --カーソル状態出力
    If (cur%ISOPEN) THEN
        SYS.DBMS_OUTPUT.PUT_LINE('カーソルオープン後:ISOPEN=true');
    ELSE
         SYS.DBMS_OUTPUT.PUT_LINE('カーソルオープン後:ISOPEN=false');   
    END IF;
    
    
    LOOP
        FETCH cur INTO rec;  
        If (cur%NOTFOUND) THEN
            SYS.DBMS_OUTPUT.PUT_LINE('NOTFOUND = true');  
            EXIT;
        END IF;
        
        SYS.DBMS_OUTPUT.PUT_LINE('フェッチした件数:ROWCOUNT=' || cur%ROWCOUNT);   
        SYS.DBMS_OUTPUT.PUT_LINE(rec.MemberId || ' ' || rec.MemberName);      
    END LOOP;
    

    --カーソルクローズ
    CLOSE cur;

    --カーソル状態出力
    If (cur%ISOPEN) THEN
        SYS.DBMS_OUTPUT.PUT_LINE('カーソルクローズ後:ISOPEN=true');
    ELSE
         SYS.DBMS_OUTPUT.PUT_LINE('カーソルクローズ後:ISOPEN=false');   
    END IF;
END;
出力結果です。
カーソルオープン前:ISOPEN=false
カーソルオープン後:ISOPEN=true
フェッチした件数:ROWCOUNT=1
1 Yamada
フェッチした件数:ROWCOUNT=2
2 Tanaka
フェッチした件数:ROWCOUNT=3
3 Suzuki
NOTFOUND = true
カーソルクローズ後:ISOPEN=false

同じく、カーソルFORループで各属性を出力するコードを実行してみました。
DECLARE
    CURSOR cur IS
        SELECT * FROM Member;
    
BEGIN 
    
    FOR rec IN  cur LOOP
        --カーソルISOPEN=出力  
        If (cur%ISOPEN) THEN  
            SYS.DBMS_OUTPUT.PUT_LINE('ISOPEN=true');  
        ELSE  
            SYS.DBMS_OUTPUT.PUT_LINE('ISOPEN=false');     
        END IF;
        --カーソルROWCOUNT出力
        SYS.DBMS_OUTPUT.PUT_LINE('フェッチした件数:ROWCOUNT=' || cur%ROWCOUNT);
        
        
        SYS.DBMS_OUTPUT.PUT_LINE(rec.MemberId);
    END LOOP;
    
    --カーソルISOPEN=出力  
    If (cur%ISOPEN) THEN  
        SYS.DBMS_OUTPUT.PUT_LINE('ISOPEN=true');  
    ELSE  
        SYS.DBMS_OUTPUT.PUT_LINE('ISOPEN=false');     
    END IF;
END;
出力結果です。
ISOPEN=true
フェッチした件数:ROWCOUNT=1
1
ISOPEN=true
フェッチした件数:ROWCOUNT=2
2
ISOPEN=true
フェッチした件数:ROWCOUNT=3
3
ISOPEN=false

2015年11月24日火曜日

PLSQL パラメータ付きカーソル

前回までのカーソルの使い方についてはコチラ
PLSQL SELECTの結果を取得する~取得結果が複数行の場合~
PLSQL カーソルを使用してデータを取得する

今回はパラメータ付きカーソルについてです。

カーソルにはパラメータを渡すことができます。
カーソルを定義する際に、カーソル名称とISの間に引数を宣言します。

カーソルFORループを使用せず、明示的にカーソルのオープン、クローズを行うパターンでの使用例


カーソルオープンする際に、カーソルのパラメータを指定する必要があります。
DECLARE
    --カーソル定義
    CURSOR member_csr(prmMemberId Member.MemberId%Type) IS 
        SELECT * FROM Member WHERE MemberId >= prmMemberId;
    --レコード変数
    member_rec member_csr%Rowtype;
BEGIN

    --カーソルオープンする際に引数を指定する
    OPEN member_csr('2');
    
    BEGIN
        
        LOOP
            FETCH member_csr INTO member_rec;
            EXIT WHEN member_csr%NOTFOUND;
            SYS.DBMS_OUTPUT.PUT_LINE(member_rec.memberid || ' ' || member_rec.membername);
        END LOOP;    
        
        --カーソルクローズ
        CLOSE member_csr;
        
    EXCEPTION
        WHEN others THEN  
           --例外が発生してもカーソルをクローズする  
           CLOSE member_csr;  
           --例外を親ブロックへ伝播する。(必要な場合のみ)  
           RAISE;  

    END;
    
END;

カーソルFORループでの使用例


こちらはループごとにパラメータを指定することができます。
DECLARE
    --カーソル定義
    CURSOR member_csr(prmMemberId Member.MemberId%Type) IS 
        SELECT * FROM Member WHERE MemberId = prmMemberId;
   
BEGIN

    FOR idx IN 1..3 LOOP
        --ループ毎にカーソル引数を指定できる
        FOR member_rec IN member_csr(idx) LOOP

            SYS.DBMS_OUTPUT.PUT_LINE(member_rec.memberid || ' ' || member_rec.membername);    

        END LOOP;

    END LOOP;

END;
カーソルのパラメータは型を指定することはできますが、精度は指定できません。
DECLARE
    CURSOR cur(id VARCHAR2) IS
        SELECT * FROM Member WHERE MemberId = id; 
    
BEGIN 
    
    FOR rec IN  cur('1') LOOP
          SYS.DBMS_OUTPUT.PUT_LINE(rec.MemberName);
    END LOOP;
END;

2015年11月19日木曜日

.NET(VB) LINQをVBで使用する際に注意すること

LINQはC#でしか書いたことがないので、VBのLINQの書き方がわかりません。
来年からしばらくはVBでの開発になるので、VBでのLINQの書き方を調べてみました。

VBでLINQを書く際の注意点です。
他にもあるかもしれませんが、とりあえず以下の2点。
  • FunctionとSubを書き分ける必要がある。
  • 匿名クラスを集計やグループ化のキーにする場合は、Keyキーワードを使用する必要がある。

以下のようなテストデータで説明します。
【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}
            }

FunctionとSubを書き分ける必要がある


C#では以下のように何も考えずに 「 => 」と書けばよかったのですが
var nameRankList = fruits.Select(itm => new { itm.Name, itm.Rank });

ameRankList.ToList().ForEach(itm =>
            { Console.WriteLine(string.Format("{0} {1}", itm.Name, itm.Rank)); });
VBでは以下のように値を返すものは Function で、値を返さないものは Sub で書き分ける必要があります。
'値を返すものはFunctionで
Dim nameRankList = fruits.Select(Function(itm) New With {itm.Name, itm.Rank})

'値を返さないものはSub
nameRankList.ToList().ForEach(Sub(itm) _
        Console.WriteLine(String.Format("{0} {1}", itm.Name, itm.Rank)))

Subと書くところを間違えてFunctionと書いてもエラーにならないようです。
ソース元はコチラ → 「C#.NET vs VB.NET 」VB.NET Action デリゲート型に Function ラムダ式を代入
こわいですねぇ
ちょと試してみましょうw

Form上にあるコントロールをすべて無効にするコードです。
Me.Controls.Cast(Of System.Windows.Forms.Control)().ToList().ForEach(Sub(itm) itm.Enabled = False)
Functionに変えてもエラーは出ません。もちろんコントロールも無効になりません。
Me.Controls.Cast(Of System.Windows.Forms.Control)().ToList().ForEach(Function(itm) itm.Enabled = False)

匿名クラスを集計やグループ化のキーにする場合は、Keyキーワードを使用する必要がある


まずC#で、果物名とランクでGroupByしてみます。
var nameList = fruits.GroupBy(itm => new { itm.Name, itm.Rank });
//--出力
//りんご A
//みかん A
//ぶどう B
//りんご B
nameList.ToList().ForEach(grp => { Console.WriteLine("{0} {1}",grp.Key.Name, grp.Key.Rank); });
次にVBのコードです。(NGパターン)
C#と同じ感覚で書くと、以下のようなコードになるかと思います。
出力結果からわかるように全然グループ化されていません。
Dim nameList = fruits.GroupBy(Function(itm) New With {itm.Name, itm.Rank})
'--出力
'りんご A
'みかん A
'ぶどう B
'りんご B
'みかん A
nameList.ToList().ForEach(Sub(grp) Console.WriteLine("{0} {1}", grp.Key.Name, grp.Key.Rank))
VBで匿名クラスをグループ化のキーに使用する場合、
キーに使用するプロパティの前に「Key」キーワードをつける必要があります。


匿名クラスのNameプロパティの前だけにKeyキーワードをつけてみます。
結果は名称だけでグループ化されます。
Dim nameList = fruits.GroupBy(Function(itm) New With {Key itm.Name, itm.Rank})
'--出力
'りんご A
'みかん A
'ぶどう B
nameList.ToList().ForEach(Sub(grp) Console.WriteLine("{0} {1}", grp.Key.Name, grp.Key.Rank))
匿名クラスのNameプロパティ、Rankプロパティの前にKeyキーワードをつけると、名称とランクでグループ化されます。
Dim nameList = fruits.GroupBy(Function(itm) New With {Key itm.Name, Key itm.Rank})
'--出力
'りんご A
'みかん A
'ぶどう B
'りんご B
nameList.ToList().ForEach(Sub(grp) Console.WriteLine("{0} {1}", grp.Key.Name, grp.Key.Rank))
うぅ~ん・・・ナンダカナァ

以下のコードはSelectで果物リストから名称とランクを抽出してます。
結果も予想通りです。
Dim nameList = fruits.Select(Function(itm) New With {itm.Name, itm.Rank})
'--出力
'りんご A
'みかん A
'ぶどう B
'りんご B
'みかん A
nameList.ToList().ForEach(Sub(itm) Console.WriteLine("{0} {1}", itm.Name, itm.Rank))
で重複を除きたいんでDistinctをくっつけると、ある意味予想通りですが、 結果は重複が除去されていません。
Dim nameList = fruits.Select(Function(itm) New With {itm.Name, itm.Rank}).Distinct()
'--出力
'りんご A
'みかん A
'ぶどう B
'りんご B
'みかん A
nameList.ToList().ForEach(Sub(itm) Console.WriteLine("{0} {1}", itm.Name, itm.Rank))
ここでも、匿名クラスの各プロパティの前にKeyキーワードを付けないといけません。
Dim nameList = fruits.Select(Function(itm) New With {Key itm.Name, Key itm.Rank}).Distinct()
'--出力
'りんご A
'みかん A
'ぶどう B
'りんご B
nameList.ToList().ForEach(Sub(itm) Console.WriteLine("{0} {1}", itm.Name, itm.Rank))
C#の匿名クラスは、インスタンス作成後に値を変更することができないイミュータブルなオブジェクトで
VBの匿名クラスは、インスタンス作成後に値を変更することができるミュータブルなオブジェクトなんだそうです。
ミュータブルなオブジェクトなので、VBではKeyキーワードを付けることによってequalsメソッドとgetHashcodeメソッドがオーバーライドされ、オブジェクトが等しいか判定しているんですね。
詳しくはコチラ → かるあ のメモ Key キーワードでは GetHashCode と Equals がオーバーライドされるみたい

つまり、匿名クラスのオブジェクトが同じかどうかを判定したいプロパティに、Keyキーワードをつけなければいけないということです。
以下のコードでは、匿名クラスオブジェクトの犬、猫、鳥は、同じポチという名前ですが、Keyキーワードを付けているプロパティが鳥だけ違います。
equalsメソッドでそれぞれのオブジェクトを比較すると、Keyが付いているプロパティの値が同じであれば、等価と判定されているのがわかります。
Dim dog = New With {Key .Name = "ポチ", .Type = "犬", .Squeak = "わんわん"}
Dim cat = New With {Key .Name = "ポチ", .Type = "猫", .Squeak = "にゃぁ"}
Dim bird = New With {.Name = "ポチ", Key .Type = "鳥", .Squeak = "ちゅんちゅん"}

'--出力
'犬と猫は等価
Console.WriteLine("犬と猫は{0}", (If(dog.Equals(cat), "等価", "等価でない")))

'--出力
'犬と鳥は等価でない
Console.WriteLine("犬と鳥は{0}", (If(dog.Equals(bird), "等価", "等価でない")))