ソートを行うためには、並べ替えを行う個々の要素についてその大小関係を調べる必要があります。 また、独自に作成したクラスをソートするためには、その大小関係を定義する必要があります。
ここでは、コレクションとソートの基本と、大小関係を定義したり比較するためのインターフェイスであるIComparable・IComparerおよび関連するクラスについて見ていきます。 また、比較演算子のオーバーロードとこれらのインターフェイスの実装についても触れています。 ここで解説するソート方法やDictionaryのソートなどについてはソートでも解説しているので、必要に応じて参照してください。
コレクションとソート
.NET Frameworkでは、ソートを行うためのメソッドとしてArray.Sort、ArrayList.Sort、List<T>.Sortなどのメソッドが用意されています。
Array.Sortメソッドは引数で指定された配列のソートを行い、ArrayList.SortメソッドとList<T>.Sortメソッドはインスタンス内に格納されている要素をソートします。 これらのメソッドは、いずれもクイックソートを行います。 以下のコードは、これらソートを行うメソッドを使った例です。
using System;
using System.Collections;
using System.Collections.Generic;
class Sample {
static void Main()
{
string[] arr = new string[] {"Charlie", "Dave", "Alice", "Eve", "Bob"};
ArrayList al = new ArrayList(arr);
List<string> l = new List<string>(arr);
Console.WriteLine("[Array.Sort]");
Print(arr);
Array.Sort(arr);
Print(arr);
Console.WriteLine("[ArrayList.Sort]");
Print(al);
al.Sort();
Print(al);
Console.WriteLine("[List.Sort]");
Print(l);
l.Sort();
Print(l);
}
static void Print(IEnumerable c)
{
foreach (string e in c) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
}
}
Imports System
Imports System.Collections
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim arr() As String = New String() {"Charlie", "Dave", "Alice", "Eve", "Bob"}
Dim al As New ArrayList(arr)
Dim l As New List(Of String)(arr)
Console.WriteLine("[Array.Sort]")
Print(arr)
Array.Sort(arr)
Print(arr)
Console.WriteLine("[ArrayList.Sort]")
Print(al)
al.Sort()
Print(al)
Console.WriteLine("[List.Sort]")
Print(l)
l.Sort()
Print(l)
End Sub
Shared Sub Print(ByVal c As IEnumerable)
For Each e As String In c
Console.Write("{0}, ", e)
Next
Console.WriteLine()
End Sub
End Class
[Array.Sort] Charlie, Dave, Alice, Eve, Bob, Alice, Bob, Charlie, Dave, Eve, [ArrayList.Sort] Charlie, Dave, Alice, Eve, Bob, Alice, Bob, Charlie, Dave, Eve, [List.Sort] Charlie, Dave, Alice, Eve, Bob, Alice, Bob, Charlie, Dave, Eve,
これらのメソッドでは、引数でIComparerインターフェイスやComparisonデリゲートなどを指定することで並べ替えの際の動作を変更することができるようになっています。 詳細については後述していきます。
ソートできる型
先の例では、文字列型の値をコレクションに格納してソートしましたが、int、double、DateTimeなど他の基本型でも同様にソートすることが出来ます。
using System;
using System.Collections;
using System.Collections.Generic;
class Sample {
static void Main()
{
int[] arr = new int[] {3, 0, 1, 4, 2};
ArrayList al = new ArrayList(arr);
List<int> l = new List<int>(arr);
Console.WriteLine("[Array.Sort]");
Print(arr);
Array.Sort(arr);
Print(arr);
Console.WriteLine("[ArrayList.Sort]");
Print(al);
al.Sort();
Print(al);
Console.WriteLine("[List.Sort]");
Print(l);
l.Sort();
Print(l);
}
static void Print(IEnumerable c)
{
foreach (int e in c) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
}
}
Imports System
Imports System.Collections
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim arr() As Integer = New Integer() {3, 0, 1, 4, 2}
Dim al As New ArrayList(arr)
Dim l As New List(Of Integer)(arr)
Console.WriteLine("[Array.Sort]")
Print(arr)
Array.Sort(arr)
Print(arr)
Console.WriteLine("[ArrayList.Sort]")
Print(al)
al.Sort()
Print(al)
Console.WriteLine("[List.Sort]")
Print(l)
l.Sort()
Print(l)
End Sub
Shared Sub Print(ByVal c As IEnumerable)
For Each e As Integer In c
Console.Write("{0}, ", e)
Next
Console.WriteLine()
End Sub
End Class
using System;
using System.Collections;
using System.Collections.Generic;
class Sample {
static void Main()
{
double[] arr = new double[] {Math.PI, 0.0, Math.E, -1.0, 2.99e8};
ArrayList al = new ArrayList(arr);
List<double> l = new List<double>(arr);
Console.WriteLine("[Array.Sort]");
Print(arr);
Array.Sort(arr);
Print(arr);
Console.WriteLine("[ArrayList.Sort]");
Print(al);
al.Sort();
Print(al);
Console.WriteLine("[List.Sort]");
Print(l);
l.Sort();
Print(l);
}
static void Print(IEnumerable c)
{
foreach (double e in c) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
}
}
Imports System
Imports System.Collections
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim arr() As Double = New Double() {Math.PI, 0.0, Math.E, -1.0, 2.99e8}
Dim al As New ArrayList(arr)
Dim l As New List(Of Double)(arr)
Console.WriteLine("[Array.Sort]")
Print(arr)
Array.Sort(arr)
Print(arr)
Console.WriteLine("[ArrayList.Sort]")
Print(al)
al.Sort()
Print(al)
Console.WriteLine("[List.Sort]")
Print(l)
l.Sort()
Print(l)
End Sub
Shared Sub Print(ByVal c As IEnumerable)
For Each e As Double In c
Console.Write("{0}, ", e)
Next
Console.WriteLine()
End Sub
End Class
using System;
using System.Collections;
using System.Collections.Generic;
class Sample {
static void Main()
{
DateTime[] arr = new DateTime[] {DateTime.MinValue, DateTime.MaxValue, new DateTime(2010, 10, 10)};
ArrayList al = new ArrayList(arr);
List<DateTime> l = new List<DateTime>(arr);
Console.WriteLine("[Array.Sort]");
Print(arr);
Array.Sort(arr);
Print(arr);
Console.WriteLine("[ArrayList.Sort]");
Print(al);
al.Sort();
Print(al);
Console.WriteLine("[List.Sort]");
Print(l);
l.Sort();
Print(l);
}
static void Print(IEnumerable c)
{
foreach (DateTime e in c) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
}
}
Imports System
Imports System.Collections
Imports System.Collections.Generic
Class Sample
Shared Sub Main()
Dim arr() As DateTime = New DateTime() {DateTime.MinValue, DateTime.MaxValue, new DateTime(2010, 10, 10)}
Dim al As New ArrayList(arr)
Dim l As New List(Of DateTime)(arr)
Console.WriteLine("[Array.Sort]")
Print(arr)
Array.Sort(arr)
Print(arr)
Console.WriteLine("[ArrayList.Sort]")
Print(al)
al.Sort()
Print(al)
Console.WriteLine("[List.Sort]")
Print(l)
l.Sort()
Print(l)
End Sub
Shared Sub Print(ByVal c As IEnumerable)
For Each e As DateTime In c
Console.Write("{0}, ", e)
Next
Console.WriteLine()
End Sub
End Class
[Array.Sort] 3, 0, 1, 4, 2, 0, 1, 2, 3, 4, [ArrayList.Sort] 3, 0, 1, 4, 2, 0, 1, 2, 3, 4, [List.Sort] 3, 0, 1, 4, 2, 0, 1, 2, 3, 4,
[Array.Sort] 3.14159265358979, 0, 2.71828182845905, -1, 299000000, -1, 0, 2.71828182845905, 3.14159265358979, 299000000, [ArrayList.Sort] 3.14159265358979, 0, 2.71828182845905, -1, 299000000, -1, 0, 2.71828182845905, 3.14159265358979, 299000000, [List.Sort] 3.14159265358979, 0, 2.71828182845905, -1, 299000000, -1, 0, 2.71828182845905, 3.14159265358979, 299000000,
[Array.Sort] 0001/01/01 0:00:00, 9999/12/31 23:59:59, 2010/10/10 0:00:00, 0001/01/01 0:00:00, 2010/10/10 0:00:00, 9999/12/31 23:59:59, [ArrayList.Sort] 0001/01/01 0:00:00, 9999/12/31 23:59:59, 2010/10/10 0:00:00, 0001/01/01 0:00:00, 2010/10/10 0:00:00, 9999/12/31 23:59:59, [List.Sort] 0001/01/01 0:00:00, 9999/12/31 23:59:59, 2010/10/10 0:00:00, 0001/01/01 0:00:00, 2010/10/10 0:00:00, 9999/12/31 23:59:59,
ソートできない型
このように、基本型ならばソートは可能なように見えます。 どのような型がソート可能となり、ソート可能とならないのか、その違いを見るために文字列をラップするクラスMyStringを作成し、先の例と同様にソートしてみます。
using System;
using System.Collections;
using System.Collections.Generic;
class MyString {
string val;
public MyString(string val)
{
this.val = val;
}
public override string ToString()
{
return val;
}
}
class Sample {
static void Main()
{
MyString[] arr = new MyString[] {
new MyString("Charlie"),
new MyString("Dave"),
new MyString("Alice"),
new MyString("Eve"),
new MyString("Bob")
};
ArrayList al = new ArrayList(arr);
List<MyString> l = new List<MyString>(arr);
Console.WriteLine("[Array.Sort]");
Print(arr);
try {
Array.Sort(arr);
Print(arr);
}
catch (Exception ex) {
Console.WriteLine(ex);
}
Console.WriteLine("[ArrayList.Sort]");
Print(al);
try {
al.Sort();
Print(al);
}
catch (Exception ex) {
Console.WriteLine(ex);
}
Console.WriteLine("[List.Sort]");
Print(l);
try {
l.Sort();
Print(l);
}
catch (Exception ex) {
Console.WriteLine(ex);
}
}
static void Print(IEnumerable c)
{
foreach (MyString e in c) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
}
}
Imports System
Imports System.Collections
Imports System.Collections.Generic
Class MyString
Dim val As String
Public Sub New(ByVal val As String)
MyClass.val = val
End Sub
Public Overrides Function ToString() As String
Return val
End Function
End Class
Class Sample
Shared Sub Main()
Dim arr() As MyString = New MyString() { _
New MyString("Charlie"), _
New MyString("Dave"), _
New MyString("Alice"), _
New MyString("Eve"), _
New MyString("Bob") _
}
Dim al As New ArrayList(arr)
Dim l As New List(Of MyString)(arr)
Console.WriteLine("[Array.Sort]")
Print(arr)
Try
Array.Sort(arr)
Print(arr)
Catch ex As Exception
Console.WriteLine(ex)
End Try
Console.WriteLine("[ArrayList.Sort]")
Print(al)
Try
al.Sort()
Print(al)
Catch ex As Exception
Console.WriteLine(ex)
End Try
Console.WriteLine("[List.Sort]")
Print(l)
Try
l.Sort()
Print(l)
Catch ex As Exception
Console.WriteLine(ex)
End Try
End Sub
Shared Sub Print(ByVal c As IEnumerable)
For Each e As MyString In c
Console.Write("{0}, ", e)
Next
Console.WriteLine()
End Sub
End Class
[Array.Sort] Charlie, Dave, Alice, Eve, Bob, System.InvalidOperationException: 配列にある 2 つの要素を比較できませんでした。 ---> System.ArgumentException: 少なくとも 1 つのオブジェクトで IComparable を実装しなければなりません。 場所 System.Collections.Comparer.Compare(Object a, Object b) 場所 System.Collections.Generic.ObjectComparer`1.Compare(T x, T y) 場所 System.Collections.Generic.ArraySortHelper`1.SwapIfGreaterWithItems(T[] keys, IComparer`1 comparer, Int32 a, Int32 b) 場所 System.Collections.Generic.ArraySortHelper`1.QuickSort(T[] keys, Int32 left, Int32 right, IComparer`1 comparer) 場所 System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer) --- 内部例外スタック トレースの終わり --- 場所 System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer) 場所 System.Array.Sort[T](T[] array, Int32 index, Int32 length, IComparer`1 comparer) 場所 System.Array.Sort[T](T[] array) 場所 Sample.Main() [ArrayList.Sort] Charlie, Dave, Alice, Eve, Bob, System.InvalidOperationException: 配列にある 2 つの要素を比較できませんでした。 ---> System.ArgumentException: 少なくとも 1 つのオブジェクトで IComparable を実装しなければなりません。 場所 System.Collections.Comparer.Compare(Object a, Object b) 場所 System.Array.SorterObjectArray.SwapIfGreaterWithItems(Int32 a, Int32 b) --- 内部例外スタック トレースの終わり --- 場所 System.Array.SorterObjectArray.SwapIfGreaterWithItems(Int32 a, Int32 b) 場所 System.Array.SorterObjectArray.QuickSort(Int32 left, Int32 right) 場所 System.Array.Sort(Array keys, Array items, Int32 index, Int32 length, IComparer comparer) 場所 System.Collections.ArrayList.Sort(Int32 index, Int32 count, IComparer comparer) 場所 System.Collections.ArrayList.Sort() 場所 Sample.Main() [List.Sort] Charlie, Dave, Alice, Eve, Bob, System.InvalidOperationException: 配列にある 2 つの要素を比較できませんでした。 ---> System.ArgumentException: 少なくとも 1 つのオブジェクトで IComparable を実装しなければなりません。 場所 System.Collections.Comparer.Compare(Object a, Object b) 場所 System.Collections.Generic.ObjectComparer`1.Compare(T x, T y) 場所 System.Collections.Generic.ArraySortHelper`1.SwapIfGreaterWithItems(T[] keys, IComparer`1 comparer, Int32 a, Int32 b) 場所 System.Collections.Generic.ArraySortHelper`1.QuickSort(T[] keys, Int32 left, Int32 right, IComparer`1 comparer) 場所 System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer) --- 内部例外スタック トレースの終わり --- 場所 System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer) 場所 System.Array.Sort[T](T[] array, Int32 index, Int32 length, IComparer`1 comparer) 場所 System.Collections.Generic.List`1.Sort(Int32 index, Int32 count, IComparer`1 comparer) 場所 Sample.Main()
実行結果を見て分かる通り、Sortメソッドを呼び出した時点で例外がスローされ、ソートは出来ませんでした。 何が原因でソート出来なかったのか、スローされた例外について詳しく見みます。 スローされた例外はInvalidOperationExceptionで、例外のメッセージは次のようになっています。
System.InvalidOperationException: 配列にある 2 つの要素を比較できませんでした。 ---> System.ArgumentException: 少なくとも 1 つのオブジェクトで IComparable を実装しなければなりません。
つまり、配列(コレクション)内にある要素を比較しようとしたが、その要素がIComparableインターフェイスを実装していないことを理由にArgumentExceptionをスローしたために、InvalidOperationExceptionとなったことがソート出来なかった原因となります。 言い換えると、ソートを行うには、その要素がIComparableを実装している必要があるということです。 ここまで例に挙げたstring、int、double、DateTimeは、すべてIComparableを実装しているためソートが可能だったのです。
IComparable
それでは、ソートを行う上で必要とされるIComparableインターフェイス(System名前空間)を実装するためにIComparableについて詳しく見ていきます。 IComparableインターフェイスを実装する場合は一つのメソッドCompareToを実装しなければなりません。 このメソッドでは、一つのobject型の引数を取り、インターフェイスを実装するインスタンスと引数のオブジェクトとの大小関係に応じて次の値を返す必要があります。
インスタンスと引数objの大小関係 | 戻り値として返す値 |
---|---|
並べ替えたときに、現在のインスタンスの方が引数objよりも前となる (thisはobjよりも小さい) |
0より小さい値 |
並べ替えたときに、現在のインスタンスと引数objは同じ位置となる (thisはobjと等しい) |
0 |
並べ替えたときに、現在のインスタンスの方が引数objよりも後となる (thisはobjよりも大きい) |
0より大きい値 |
早速、上記の仕様を満たすようIComparableを実装したクラスを用意し、ソートしてみます。 次のコードでは、メンバにIDと名前を持つクラスAccountを作成し、IComparableを実装しています。
using System;
using System.Collections;
class Account : IComparable {
int id;
string name;
public Account(int id, string name)
{
this.id = id;
this.name = name;
}
// IComparable.CompareToの実装
public int CompareTo(object obj)
{
// 引数がnullの場合はArgumentNullExceptionをスローする
if (obj == null) throw new ArgumentNullException();
// 引数をAccountに型変換
Account other = obj as Account;
// 引数をAccountに型変換できなかった場合はArgumentExceptionをスローする
if (other == null) throw new ArgumentException();
// フィールドidの値の大小関係をCompareToの戻り値とする
if (this.id < other.id) {
// 現在のインスタンスは、引数よりも小さい
return -1;
}
else if (this.id > other.id) {
// 現在のインスタンスは、引数よりも大きい
return 1;
}
else {
// 現在のインスタンスは、引数よりは等しい
return 0;
}
// より簡単に、以下のように記述することもできる
//return this.id - other.id;
// また、Integer.CompareToメソッドの結果を使用するように記述することもできる
//return this.id.CompareTo(other.id);
}
public override string ToString()
{
return string.Format("{0}:{1}", id, name);
}
}
class Sample {
static void Main()
{
Account[] arr = new Account[] {
new Account(1, "Eve"),
new Account(4, "Dave"),
new Account(2, "Alice"),
new Account(0, "Charlie"),
new Account(3, "Bob"),
};
Console.WriteLine("[before sort]");
Print(arr);
Console.WriteLine("[after sort]");
Array.Sort(arr);
Print(arr);
}
static void Print(IEnumerable c)
{
foreach (Account e in c) {
Console.WriteLine(e);
}
}
}
Imports System
Imports System.Collections
Class Account
Implements IComparable
Dim id As Integer
Dim name As String
Public Sub New(ByVal id As Integer, ByVal name As String)
MyClass.id = id
MyClass.name = name
End Sub
' IComparable.CompareToの実装
Public Function CompareTo(ByVal obj As Object) As Integer Implements IComparable.CompareTo
' 引数がNothingの場合はArgumentNullExceptionをスローする
If obj Is Nothing Then Throw New ArgumentNullException()
' 引数をAccountに型変換
Dim other As Account = TryCast(obj, Account)
' 引数をAccountに型変換できなかった場合はArgumentExceptionをスローする
If other Is Nothing Then Throw New ArgumentException()
' フィールドidの値の大小関係をCompareToの戻り値とする
If MyClass.id < other.id Then
' 現在のインスタンスは、引数よりも小さい
Return -1
Else IF MyClass.id > other.id Then
' 現在のインスタンスは、引数よりも大きい
Return 1
Else
' 現在のインスタンスは、引数よりは等しい
Return 0
End If
' より簡単に、以下のように記述することもできる
'Return MyClass.id - other.id
' また、Integer.CompareToメソッドの結果を使用するように記述することもできる
'Return MyClass.id.CompareTo(other.id)
End Function
Public Overrides Function ToString() As String
Return String.Format("{0}:{1}", id, name)
End Function
End Class
Class Sample
Shared Sub Main()
Dim arr() As Account = New Account() { _
New Account(1, "Eve"), _
New Account(4, "Dave"), _
New Account(2, "Alice"), _
New Account(0, "Charlie"), _
New Account(3, "Bob") _
}
Console.WriteLine("[before sort]")
Print(arr)
Console.WriteLine("[after sort]")
Array.Sort(arr)
Print(arr)
End Sub
Shared Sub Print(ByVal c As IEnumerable)
For Each e As Account In c
Console.WriteLine(e)
Next
End Sub
End Class
[before sort] 1:Eve 4:Dave 2:Alice 0:Charlie 3:Bob [after sort] 0:Charlie 1:Eve 2:Alice 3:Bob 4:Dave
結果を見てわかるとおり、ソートの際にInvalidOperationExceptionがスローされることもなく、Accountクラスのidフィールドの値にしたがってソート出来ていることが分かると思います。
この例ではArray.Sortメソッドを使ったため、Accountクラスのインスタンス同士のみの比較しか行われませんが、ArrayListなどを使う場合はすべて同じ型同士の比較になるとは限りません。 IComparable.CompareToメソッドは引数がobject型であるため、どのような型との比較になるかは呼び出し元次第となります。 そのため、IComparableインターフェイスを実装する側で引数の型をチェックする必要があります。 上記のコードでは異なる型との比較ではArgumentExceptionをスローするようにしていますが、場合によっては例外とはせず適切な比較結果を返すように実装する事も可能です。
また、null(Nothing)との比較についても、上記のコードではArgumentNullExceptionをスローするようにしていますが、string型のようにnullはすべてのオブジェクトよりも小さいと定義することもできます。
IComparer
IComparableに似たインターフェイスとして、.NET FrameworkにはIComparerインターフェイス(System.Collections名前空間)が用意されています。 IComparableがインターフェイスを実装するオブジェクトと他のオブジェクトとの比較処理を提供するのに対し、IComparerは任意の二つのオブジェクトの比較処理を提供するためのものです。 言い換えると、IComparableでは比較処理は常に比較される対象と結びついていますが、IComparerでは比較処理と比較される対象を分離させることが出来ます。
このことにどのような利点があるかというと、例えば先に例に挙げたAccountクラスでは名前とIDのフィールドを持っていますが、IComparableで実装したのはIDによる比較のみです。 これを名前順やIDの降順で並べ替えたりしたい場合、IComparableの実装を書き換える必要が出てきますが、IComparerを使うことで実装を書き換えずに新たな比較処理を個別に用意することができるようになります。
IComparerインターフェイスを実装する場合は一つのメソッドCompareを実装しなければなりません。 IComparable.CompareToメソッドが一つのobject型の引数を取るのに対し、IComparer.Compareメソッドでは二つのobject型の引数を取ります。 戻り値は、IComparable.CompareToメソッドと同様、引数同士の大小関係に応じて次の値を返す必要があります。
引数xとyの大小関係 | 戻り値として返す値 |
---|---|
並べ替えたときに、引数xが引数yよりも前となる (xはyよりも小さい) |
0より小さい値 |
並べ替えたときに、引数xが引数yと同じ位置となる (xはyと等しい) |
0 |
並べ替えたときに、引数xが引数yよりも後となる (xはyよりも大きい) |
0より大きい値 |
早速、上記の仕様を満たすようIComparerを実装したクラスを用意し、ソートしてみます。 次のコードでは、IComparerを実装してint型の配列を昇順・降順で並べ替えるためのクラスを作成し、ソートに使っています。
using System;
using System.Collections;
// intを昇順で並べ替えるIComparerの実装
class AscendingOrderComparer : IComparer {
public int Compare(object x, object y)
{
int ix = (int)x;
int iy = (int)y;
return ix - iy;
// 次のように記述することもできる
//return ix.CompareTo(iy);
}
}
// intを降順で並べ替えるIComparerの実装
class DescendingOrderComparer : IComparer {
public int Compare(object x, object y)
{
int ix = (int)x;
int iy = (int)y;
return iy - ix;
// 次のように記述することもできる
//return iy.CompareTo(ix);
//return -ix.CompareTo(iy);
}
}
class Sample {
static void Main()
{
int[] arr = new int[] {3, 4, 2, 0, 1};
Console.WriteLine("[before sort]");
Print(arr);
// AscendingOrderComparerを使ってソート
Console.WriteLine("[AscendingOrderComparer]");
Array.Sort(arr, new AscendingOrderComparer());
Print(arr);
// DescendingOrderComparerを使ってソート
Console.WriteLine("[DescendingOrderComparer]");
Array.Sort(arr, new DescendingOrderComparer());
Print(arr);
// Array.SortメソッドとArray.Reverseメソッドを使ってソート
Console.WriteLine("[Sort + Reverse]");
Array.Sort(arr);
Print(arr);
Array.Reverse(arr);
Print(arr);
}
static void Print(IEnumerable c)
{
foreach (int e in c) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
}
}
Imports System
Imports System.Collections
' Integerを昇順で並べ替えるIComparerの実装
Class AscendingOrderComparer
Implements IComparer
Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare
Dim ix As Integer = CInt(x)
Dim iy As Integer = CInt(y)
Return ix - iy
' 次のように記述することもできる
'Return ix.CompareTo(iy)
End Function
End Class
' Integerを降順で並べ替えるIComparerの実装
Class DescendingOrderComparer
Implements IComparer
Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare
Dim ix As Integer = CInt(x)
Dim iy As Integer = CInt(y)
Return iy - ix
' 次のように記述することもできる
'Return iy.CompareTo(ix)
'Return -ix.CompareTo(iy)
End Function
End Class
Class Sample
Shared Sub Main()
Dim arr() As Integer = New Integer() {3, 4, 2, 0, 1}
Console.WriteLine("[before sort]")
Print(arr)
' AscendingOrderComparerを使ってソート
Console.WriteLine("[AscendingOrderComparer]")
Array.Sort(arr, New AscendingOrderComparer())
Print(arr)
' DescendingOrderComparerを使ってソート
Console.WriteLine("[DescendingOrderComparer]")
Array.Sort(arr, New DescendingOrderComparer())
Print(arr)
' Array.SortメソッドとArray.Reverseメソッドを使ってソート
Console.WriteLine("[Sort + Reverse]")
Array.Sort(arr)
Print(arr)
Array.Reverse(arr)
Print(arr)
End Sub
Shared Sub Print(ByVal c As IEnumerable)
For Each e As Integer In c
Console.Write("{0}, ", e)
Next
Console.WriteLine()
End Sub
End Class
[before sort] 3, 4, 2, 0, 1, [AscendingOrderComparer] 0, 1, 2, 3, 4, [DescendingOrderComparer] 4, 3, 2, 1, 0, [Sort + Reverse] 0, 1, 2, 3, 4, 4, 3, 2, 1, 0,
Array.Sortメソッドでは引数にIComparerを指定すると、そのIComparerを使って並べ替えが行われるようになります。 その結果、上記のように昇順・降順でのソートが行えるようになります。 なお、上記のコードでは、比較のためにIComparerを使わずArray.SortメソッドとArray.Reverseメソッドを使って降順にソートする例も記述しています。
もう少し違った例を挙げてみます。 先ほどのAccountクラスと、これを比較するためのAccountComparerクラスを用意してソートします。 AccountComparerには、比較の際に昇順・降順および名前順・ID順をオプションとして選べるようプロパティを用意します。
using System;
using System.Collections;
class Account {
private int id;
private string name;
public int ID {
get { return id; }
}
public string Name {
get { return name; }
}
public Account(int id, string name)
{
this.id = id;
this.name = name;
}
public override string ToString()
{
return string.Format("{0}:{1}", id, name);
}
}
class AccountComparer : IComparer {
public enum SortMode {
ByID, // ID順でソート
ByName, // 名前順でソート
}
public enum SortOrder : int {
Ascending = +1, // 昇順でソート
Descending = -1, // 降順でソート
}
private SortMode mode = SortMode.ByID;
private SortOrder order = SortOrder.Ascending;
public SortMode Mode {
get { return mode; }
set { mode = value; }
}
public SortOrder Order {
get { return order; }
set { order = value; }
}
public int Compare(object x, object y)
{
// 引数がnullの場合はArgumentNullExceptionをスローする
if (x == null) throw new ArgumentNullException("x");
if (y == null) throw new ArgumentNullException("y");
Account ax = x as Account;
Account ay = y as Account;
// Accountクラスに型変換出来ない場合は、ArgumentExceptionをスローする
if (ax == null) throw new ArgumentException();
if (ay == null) throw new ArgumentException();
switch (mode) {
case SortMode.ByID:
return (int)order * (ax.ID - ay.ID);
case SortMode.ByName:
return (int)order * string.Compare(ax.Name, ay.Name);
default:
throw new InvalidOperationException("未定義のSortMode");
}
}
}
class Sample {
static void Main()
{
Account[] arr = new Account[] {
new Account(1, "Eve"),
new Account(4, "Dave"),
new Account(2, "Alice"),
new Account(0, "Charlie"),
new Account(3, "Bob"),
};
AccountComparer comparer = new AccountComparer();
Print(arr);
Console.WriteLine("[{0} {1}]", comparer.Mode, comparer.Order);
Array.Sort(arr, comparer);
Print(arr);
comparer.Mode = AccountComparer.SortMode.ByName;
Console.WriteLine("[{0} {1}]", comparer.Mode, comparer.Order);
Array.Sort(arr, comparer);
Print(arr);
comparer.Order = AccountComparer.SortOrder.Descending;
Console.WriteLine("[{0} {1}]", comparer.Mode, comparer.Order);
Array.Sort(arr, comparer);
Print(arr);
}
static void Print(IEnumerable c)
{
foreach (Account e in c) {
Console.WriteLine(e);
}
}
}
Imports System
Imports System.Collections
Class Account
Private _id As Integer
Private _name As String
Public ReadOnly Property ID As Integer
Get
Return _id
End Get
End Property
Public ReadOnly Property Name As String
Get
Return _name
End Get
End Property
Public Sub New(ByVal id As Integer, ByVal name As String)
_id = id
_name = name
End Sub
Public Overrides Function ToString() As String
Return String.Format("{0}:{1}", _id, _name)
End Function
End Class
Class AccountComparer
Implements IComparer
Public Enum SortMode
ByID ' ID順でソート
ByName ' 名前順でソート
End Enum
Public Enum SortOrder As Integer
Ascending = +1 ' 昇順でソート
Descending = -1 ' 降順でソート
End Enum
Private _mode As SortMode = SortMode.ByID
Private _order As SortOrder = SortOrder.Ascending
Public Property Mode As SortMode
Get
Return _mode
End Get
Set(ByVal value As SortMode)
_mode = value
End Set
End Property
Public Property Order As SortOrder
Get
Return _order
End Get
Set(ByVal value As SortOrder)
_order = value
End Set
End Property
Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare
' 引数がNothingの場合はArgumentNullExceptionをスローする
If x Is Nothing Then Throw New ArgumentNullException("x")
If y Is Nothing Then Throw New ArgumentNullException("y")
Dim ax As Account = TryCast(x, Account)
Dim ay As Account = TryCast(y, Account)
' Accountクラスに型変換出来ない場合は、ArgumentExceptionをスローする
If ax Is Nothing Then Throw New ArgumentException()
If ay Is Nothing Then Throw New ArgumentException()
Select Case _mode
Case SortMode.ByID
Return CInt(_order) * (ax.ID - ay.ID)
Case SortMode.ByName
Return CInt(_order) * string.Compare(ax.Name, ay.Name)
Case Else
Throw New InvalidOperationException("未定義のSortMode")
End Select
End Function
End Class
Class Sample
Shared Sub Main()
Dim arr() As Account = New Account() { _
New Account(1, "Eve"), _
New Account(4, "Dave"), _
New Account(2, "Alice"), _
New Account(0, "Charlie"), _
New Account(3, "Bob") _
}
Dim comparer As New AccountComparer()
Print(arr)
Console.WriteLine("[{0} {1}]", comparer.Mode, comparer.Order)
Array.Sort(arr, comparer)
Print(arr)
comparer.Mode = AccountComparer.SortMode.ByName
Console.WriteLine("[{0} {1}]", comparer.Mode, comparer.Order)
Array.Sort(arr, comparer)
Print(arr)
comparer.Order = AccountComparer.SortOrder.Descending
Console.WriteLine("[{0} {1}]", comparer.Mode, comparer.Order)
Array.Sort(arr, comparer)
Print(arr)
End Sub
Shared Sub Print(ByVal c As IEnumerable)
For Each e As Account In c
Console.WriteLine(e)
Next
End Sub
End Class
1:Eve 4:Dave 2:Alice 0:Charlie 3:Bob [ByID Ascending] 0:Charlie 1:Eve 2:Alice 3:Bob 4:Dave [ByName Ascending] 2:Alice 3:Bob 0:Charlie 4:Dave 1:Eve [ByName Descending] 1:Eve 4:Dave 0:Charlie 3:Bob 2:Alice
この例を見てわかるとおり、比較される側であるAccountクラスでは、必ずしもIComparerを実装している必要はありません。 また、IComparerを実装する側では、比較対象に合わせて比較処理を定義することができます。
IComparable<T>
IComparable<T>インターフェイス(System名前空間)はIComparableインターフェイスのジェネリック版で、型パラメータTで指定された型との比較が可能であることを表すインターフェイスです。 IComparableと同様CompareToメソッドを実装する必要がありますが、引数の型はobjectではなく型パラメータで指定された型となるため、実装に際して型のチェックをする必要が無くなります。
以下の例は、IComparableでの例をIComparable<T>インターフェイス使ったものに書き換えたものです。
using System;
using System.Collections.Generic;
class Account : IComparable<Account> {
int id;
string name;
public Account(int id, string name)
{
this.id = id;
this.name = name;
}
// IComparable<Account>.CompareToの実装
public int CompareTo(Account other)
{
// 引数がnullの場合はArgumentNullExceptionをスローする
if (other == null) throw new ArgumentNullException("other");
// フィールドidの値の大小関係をCompareToの戻り値とする
return this.id - other.id;
}
public override string ToString()
{
return string.Format("{0}:{1}", id, name);
}
}
class Sample {
static void Main()
{
Account[] arr = new Account[] {
new Account(1, "Eve"),
new Account(4, "Dave"),
new Account(2, "Alice"),
new Account(0, "Charlie"),
new Account(3, "Bob"),
};
Console.WriteLine("[before sort]");
Print(arr);
Console.WriteLine("[after sort]");
Array.Sort(arr);
Print(arr);
}
static void Print(IEnumerable<Account> c)
{
foreach (Account e in c) {
Console.WriteLine(e);
}
}
}
Imports System
Imports System.Collections.Generic
Class Account
Implements IComparable(Of Account)
Dim id As Integer
Dim name As String
Public Sub New(ByVal id As Integer, ByVal name As String)
MyClass.id = id
MyClass.name = name
End Sub
' IComparable(Of Account).CompareToの実装
Public Function CompareTo(ByVal other As Account) As Integer Implements IComparable(Of Account).CompareTo
' 引数がNothingの場合はArgumentNullExceptionをスローする
If other Is Nothing Then Throw New ArgumentNullException("other")
' フィールドidの値の大小関係をCompareToの戻り値とする
Return MyClass.id - other.id
End Function
Public Overrides Function ToString() As String
Return String.Format("{0}:{1}", id, name)
End Function
End Class
Class Sample
Shared Sub Main()
Dim arr() As Account = New Account() { _
New Account(1, "Eve"), _
New Account(4, "Dave"), _
New Account(2, "Alice"), _
New Account(0, "Charlie"), _
New Account(3, "Bob") _
}
Console.WriteLine("[before sort]")
Print(arr)
Console.WriteLine("[after sort]")
Array.Sort(arr)
Print(arr)
End Sub
Shared Sub Print(ByVal c As IEnumerable(Of Account))
For Each e As Account In c
Console.WriteLine(e)
Next
End Sub
End Class
[before sort] 1:Eve 4:Dave 2:Alice 0:Charlie 3:Bob [after sort] 0:Charlie 1:Eve 2:Alice 3:Bob 4:Dave
実装は省略しますが、次のコードのように型パラメータTで指定する型を変えて複数のIComparable<T>を実装することも出来ます。
using System;
class Account : IComparable<Account>, IComparable<int>, IComparable<string> {
// IComparable<Account>.CompareToの実装
public int CompareTo(Account other)
{
// Accountとの比較処理
}
// IComparable<int>.CompareToの実装
public int CompareTo(int other)
{
// intとの比較処理
}
// IComparable<string>.CompareToの実装
public int CompareTo(string other)
{
// stringとの比較処理
}
}
Imports System
Class Account
Implements IComparable(Of Account)
Implements IComparable(Of Integer)
Implements IComparable(Of String)
' IComparable(Of Account).CompareToの実装
Public Function CompareTo(ByVal other As Account) As Integer Implements IComparable(Of Account).CompareTo
' Accountとの比較処理
End Function
' IComparable(Of Integer).CompareToの実装
Public Function CompareTo(ByVal other As Integer) As Integer Implements IComparable(Of Integer).CompareTo
' Integerとの比較処理
End Function
' IComparable(Of String).CompareToの実装
Public Function CompareTo(ByVal other As String) As Integer Implements IComparable(Of String).CompareTo
' Stringとの比較処理
End Function
End Class
IComparer<T>
IComparer<T>インターフェイス(System.Collections.Generic名前空間)はIComparerインターフェイスのジェネリック版で、型パラメータTで指定された型に特化した比較処理を提供するためのインターフェイスです。 IComparerと同様Compareメソッドを実装する必要がありますが、引数の型はobjectではなく型パラメータで指定された型となるため、実装に際して型のチェックをする必要が無くなります。
以下の例は、IComparerでの例をIComparer<T>インターフェイス使ったものに書き換えたものです。
using System;
using System.Collections.Generic;
// 昇順で並べ替えるIComparer<int>の実装
class AscendingOrderComparer : IComparer<int> {
public int Compare(int x, int y)
{
return x - y;
}
}
// 降順で並べ替えるIComparer<int>の実装
class DescendingOrderComparer : IComparer<int> {
public int Compare(int x, int y)
{
return y - x;
}
}
class Sample {
static void Main()
{
int[] arr = new int[] {3, 4, 2, 0, 1};
Console.WriteLine("[before sort]");
Print(arr);
// AscendingOrderComparerを使ってソート
Console.WriteLine("[AscendingOrderComparer]");
Array.Sort(arr, new AscendingOrderComparer());
Print(arr);
// DescendingOrderComparerを使ってソート
Console.WriteLine("[DescendingOrderComparer]");
Array.Sort(arr, new DescendingOrderComparer());
Print(arr);
}
static void Print(IEnumerable<int> c)
{
foreach (int e in c) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
}
}
Imports System
Imports System.Collections.Generic
' 昇順で並べ替えるIComparer(Of Integer)の実装
Class AscendingOrderComparer
Implements IComparer(Of Integer)
Public Function Compare(ByVal x As Integer, ByVal y As Integer) As Integer Implements IComparer(Of Integer).Compare
Return x - y
End Function
End Class
' 降順で並べ替えるIComparer(Of Integer)の実装
Class DescendingOrderComparer
Implements IComparer(Of Integer)
Public Function Compare(ByVal x As Integer, ByVal y As Integer) As Integer Implements IComparer(Of Integer).Compare
Return y - x
End Function
End Class
Class Sample
Shared Sub Main()
Dim arr() As Integer = New Integer() {3, 4, 2, 0, 1}
Console.WriteLine("[before sort]")
Print(arr)
' AscendingOrderComparerを使ってソート
Console.WriteLine("[AscendingOrderComparer]")
Array.Sort(arr, New AscendingOrderComparer())
Print(arr)
' DescendingOrderComparerを使ってソート
Console.WriteLine("[DescendingOrderComparer]")
Array.Sort(arr, New DescendingOrderComparer())
Print(arr)
End Sub
Shared Sub Print(ByVal c As IEnumerable(Of Integer))
For Each e As Integer In c
Console.Write("{0}, ", e)
Next
Console.WriteLine()
End Sub
End Class
[before sort] 3, 4, 2, 0, 1, [AscendingOrderComparer] 0, 1, 2, 3, 4, [DescendingOrderComparer] 4, 3, 2, 1, 0,
Comparison<T>
Comparison<T>デリゲート(System名前空間)は、二つのオブジェクトを比較するメソッド、つまりIComparer<T>.Compareメソッドのような比較処理を行うメソッドを表すデリゲートです。 Array.SortおよびList<T>.Sortメソッドはこのデリゲートを引数に取ることができ、ソートの際の比較処理に使用することが出来ます。
このデリゲートを用いる利点は、IComparer<T>やIComparable<T>を実装したクラスを用意しなくても、比較処理を定義したメソッドを用意するだけでソートが行える点にあります。 このデリゲートと匿名メソッドやラムダ式を組み合わせて用いることでより簡単に比較処理を記述することも出来ます。
using System;
using System.Collections.Generic;
class Sample {
// int型の値を降順で比較するメソッド
static int CompareDescendingOrder(int x, int y)
{
return y - x;
}
static void Main()
{
int[] arr = new int[] {3, 4, 2, 0, 1};
Console.WriteLine("[before sort]");
Print(arr);
// Comparison<int>を指定せずにソート
Console.WriteLine("[Sort]");
Array.Sort(arr);
Print(arr);
// Comparison<int>を指定してソート
Comparison<int> comparison = CompareDescendingOrder;
Console.WriteLine("[Sort + Comparison<int>]");
Array.Sort(arr, comparison);
// 単に次のように記述することも出来る
//Array.Sort(arr, CompareDescendingOrder);
// 匿名メソッドを使った場合は次のように記述出来る
//Array.Sort(arr, delegate(int x, int y) {
// return y, x
//});
// ラムダ式を使った場合は次のように記述出来る
//Array.Sort(arr, (x, y) => y - x);
Print(arr);
}
static void Print(IEnumerable<int> c)
{
foreach (int e in c) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
}
}
Imports System
Imports System.Collections.Generic
Class Sample
' Integer型の値を降順で比較するメソッド
Shared Function CompareDescendingOrder(ByVal x As Integer, ByVal y As Integer) As Integer
Return y - x
End Function
Shared Sub Main()
Dim arr() As Integer = New Integer() {3, 4, 2, 0, 1}
Console.WriteLine("[before sort]")
Print(arr)
' Comparison(Of Integer)を指定せずにソート
Console.WriteLine("[Sort]")
Array.Sort(arr)
Print(arr)
' Comparison(Of Integer)を指定してソート
Dim comparison As Comparison(Of Integer) = AddressOf CompareDescendingOrder
Console.WriteLine("[Sort + Comparison(Of Integer)]")
Array.Sort(arr, comparison)
' 単に次のように記述することも出来る
'Array.Sort(arr, AddressOf CompareDescendingOrder)
' ラムダ式を使った場合は次のように記述出来る
'Array.Sort(arr, Function(x, y) y - x)
Print(arr)
End Sub
Shared Sub Print(ByVal c As IEnumerable(Of Integer))
For Each e As Integer In c
Console.Write("{0}, ", e)
Next
Console.WriteLine()
End Sub
End Class
[before sort] 3, 4, 2, 0, 1, [Sort] 0, 1, 2, 3, 4, [Sort + Comparison(Of Integer)] 4, 3, 2, 1, 0,
Comparer<T>
Comparer<T>クラス(System.Collections.Generic名前空間)は、IComparer非ジェネリックインターフェイスとIComparer<T>ジェネリックインターフェイスを実装する抽象クラスです。 このクラスの機能と存在意義はIComparer<T>と同じですが、IComparerとIComparer<T>の二つのインターフェイスを実装するクラスを作成しなくても、このクラスを継承して抽象メソッドであるCompareメソッドを実装するだけで同じ機能を提供することができるようになります。
ジェネリックな比較処理を提供することを考えたとき、ArrayList.Sortなど非ジェネリックなコレクションのソートでも動作するようにしたい場合は、IComparer<T>だけでなくIComparerも実装する必要があります。 Comparer<T>クラスでは引数の型のチェックなどは既に実装されているため、簡単にジェネリック・非ジェネリック両方の比較処理を提供できるようになります。
以下の例では、Comparer<T>を継承したクラスを作成し、ArrayList.Sortでのソートで使用しています。 ソートしようとしているArrayListの要素にnull(Nothing)が含まれている点に注目してください。
using System;
using System.Collections;
using System.Collections.Generic;
// int型の値を降順で並べ替えるComparer
class DescendingOrderComparer : Comparer<int> {
public override int Compare(int x, int y)
{
return y - x;
}
}
class Sample {
static void Main()
{
ArrayList al = new ArrayList(new object[] {3, null, 4, 2, 0, 1});
Console.WriteLine("[before sort]");
Print(al);
Console.WriteLine("[after sort]");
al.Sort(new DescendingOrderComparer());
Print(al);
}
static void Print(IEnumerable c)
{
foreach (object e in c) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
}
}
Imports System
Imports System.Collections
Imports System.Collections.Generic
' Integer型の値を降順で並べ替えるComparer
Class DescendingOrderComparer
Inherits Comparer(Of Integer)
Public Overrides Function Compare(ByVal x As Integer, ByVal y As Integer) As Integer
Return y - x
End Function
End Class
Class Sample
Shared Sub Main()
Dim al As New ArrayList(New Object() {3, Nothing, 4, 2, 0, 1})
Console.WriteLine("[before sort]")
Print(al)
Console.WriteLine("[after sort]")
al.Sort(New DescendingOrderComparer())
Print(al)
End Sub
Shared Sub Print(ByVal c As IEnumerable)
For Each e As Object In c
Console.Write("{0}, ", e)
Next
Console.WriteLine()
End Sub
End Class
[before sort] 3, , 4, 2, 0, 1, [after sort] , 4, 3, 2, 1, 0,
なお、Comparer<T>クラスで実装されているIComparer.Compareの比較処理では、null(Nothing)はnull以外のどのような値よりも小さいとみなされます。 この動作を変えたい場合は、派生クラスでIComparer.Compareを再実装する必要があります。
Comparer.Create
.NET Framework 4.5からは、任意のComparison<T>デリゲートからComparer<T>クラスのインスタンスを作成するメソッドComparer<T>.Createが追加されました。 このメソッドを使うことにより、Comparer<T>を継承したクラスを作成しなくても、Comparison<T>デリゲートをIComparer<T>として使用することができるようになります。
このメソッドは、IComparer<T>インターフェイスはサポートしているがComparison<T>デリゲートはサポートしていないクラスやメソッドを使用する際に特に便利です。 例えばSortedListでは、コンストラクタにIComparer<T>を指定することで要素のソート順を変更することができますが、Comparison<T>デリゲートを直接指定することはできません。 そのため、既存の比較用メソッドをSortedListで使用しようとした場合、メソッドをComparer<T>を継承(もしくはIComparer<T>を実装)したクラスに書き換える必要がありますが、Comparer<T>.Createメソッドを使えばコードを一切変更することなくそのまま活用することができます。
次の例では、int型の値を降順で比較するメソッドからComparer<int>を作成し、SortedListの並び替え順として使用するようにしています。 これにより、SortedListの内容はキーを降順にした並べ替えられます。
using System;
using System.Collections.Generic;
class Sample {
// int型の値を降順で比較するメソッド
static int CompareDescendingOrder(int x, int y)
{
return y - x;
}
static void Main()
{
// メソッドからComparer<int>を作成
Comparer<int> comparer = Comparer<int>.Create(CompareDescendingOrder);
// 作成したComparer<int>を指定してSortedListを作成
SortedList<int, string> idAndName = new SortedList<int, string>(comparer);
idAndName.Add(1, "Eve");
idAndName.Add(4, "Dave");
idAndName.Add(2, "Alice");
idAndName.Add(0, "Charlie");
idAndName.Add(3, "Bob");
// SortedListの内容を表示
foreach (KeyValuePair<int, string> pair in idAndName) {
Console.WriteLine("{0}:{1}", pair.Key, pair.Value);
}
}
}
Imports System
Imports System.Collections.Generic
Class Sample
' Integer型の値を降順で比較するメソッド
Shared Function CompareDescendingOrder(ByVal x As Integer, ByVal y As Integer) As Integer
Return y - x
End Function
Shared Sub Main()
' メソッドからComparer(Of Integer)を作成
Dim comparer As Comparer(Of Integer) = Comparer(Of Integer).Create(AddressOf CompareDescendingOrder)
' 作成したComparer(Of Integer)を指定してSortedListを作成
Dim idAndName As New SortedList(Of Integer, String)(comparer)
idAndName.Add(1, "Eve")
idAndName.Add(4, "Dave")
idAndName.Add(2, "Alice")
idAndName.Add(0, "Charlie")
idAndName.Add(3, "Bob")
' SortedListの内容を表示
For Each pair As KeyValuePair(Of Integer, String) In idAndName
Console.WriteLine("{0}:{1}", pair.Key, pair.Value)
Next
End Sub
End Class
4:Dave 3:Bob 2:Alice 1:Eve 0:Charlie
StringComparer
StringComparerクラス(System名前空間)は、文字列比較に特化したIComparerの実装です。 大文字小文字を無視した比較、現在のカルチャ・インバリアントカルチャの規則に基づいた比較などを行う実装が用意されています。 詳細については、StringComparerについての個別の解説を参照してください。
using System;
class Sample {
static void Main()
{
string[] arr = new string[] {"亜", "絵", "井", "尾", "宇"};
Print(arr);
Console.WriteLine("[StringComparer.CurrentCulture]");
Array.Sort(arr, StringComparer.CurrentCulture);
Print(arr);
Console.WriteLine("[StringComparer.InvariantCulture]");
Array.Sort(arr, StringComparer.InvariantCulture);
Print(arr);
}
static void Print(string[] arr)
{
foreach (string e in arr) {
Console.Write("{0}, ", e);
}
Console.WriteLine();
}
}
Imports System
Class Sample
Shared Sub Main()
Dim arr() As String = New String() {"亜", "絵", "井", "尾", "宇"}
Print(arr)
Console.WriteLine("[StringComparer.CurrentCulture]")
Array.Sort(arr, StringComparer.CurrentCulture)
Print(arr)
Console.WriteLine("[StringComparer.InvariantCulture]")
Array.Sort(arr, StringComparer.InvariantCulture)
Print(arr)
End Sub
Shared Sub Print(ByVal arr() As String)
For Each e As Object In arr
Console.Write("{0}, ", e)
Next
Console.WriteLine()
End Sub
End Class
亜, 絵, 井, 尾, 宇, [StringComparer.CurrentCulture] 亜, 井, 宇, 絵, 尾, [StringComparer.InvariantCulture] 井, 亜, 宇, 尾, 絵,