標準クラスライブラリ中のインターフェース
.NET Framework の標準クラスライブラリでは、汎用性の高いいくつかのインターフェースを標準で用意しています。 ここでは、そのうちのいくつかを紹介します。
IComparable
IComparable<T>
インターフェイス(System
名前空間)は、順序比較ができるものを表します。
配列の整列などに使います。
using System;
using System.Linq;
/// <summary>
/// 2次元上の点。
/// <see cref="IComparable{T}"/> を実装している = 順序をつけられる。
/// </summary>
class Point2D : IComparable<Point2D>
{
public double X { get; }
public double Y { get; }
public Point2D(double x, double y)
{
X = x;
Y = y;
}
public double Radius => Math.Sqrt(X * X + Y * Y);
public double Angle => Math.Atan2(Y, X);
/// <summary>
/// 距離で順序を決める。
/// 距離が全く同じなら偏角で順序付け。
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public int CompareTo(Point2D other)
{
var r = Radius.CompareTo(other.Radius);
if (r != 0) return r;
return Angle.CompareTo(other.Angle);
}
}
class IComparableSample
{
public static void Main()
{
const int N = 5;
var rand = new Random();
var data = Enumerable.Range(0, N).Select(_ => new Point2D(rand.NextDouble(), rand.NextDouble())).ToArray();
Console.WriteLine("元:");
foreach (var p in data) WriteLine(p);
// 並べ替えの順序に使える
Console.WriteLine("整列済み:");
foreach (var p in data.OrderBy(x => x)) WriteLine(p);
}
private static void WriteLine(Point2D p)
{
Console.WriteLine($"({p.X:N3}, {p.Y:N3}), radius = {p.Radius:N3}, angle = {p.Angle:N3}");
}
}
コレクション
コレクション(参考: 「コレクション概要」)には、 同じ操作ができる様々な実装方法があります(それぞれにメリット・デメリット、適切な利用場面があります)。
そして、C#では、操作の種類ごとにインターフェイスが標準で用意されていて、コレクションはそれらのインターフェイスを実装します。
以下の表示いくつか例を挙げます(いずれもSystem.Collections.Generic
名前空間)。
(詳しくはMSDNをご覧ください。)
インターフェイス | 説明 |
---|---|
IEnumerable<T>
|
要素の列挙ができる。foreach ステートメントや、LINQ to Objects で使える。
|
ICollection<T>
|
IEnumerable<T> に加えて、要素の追加(Add )、削除(Remove )などができたり、要素の個数が取れる。
|
IList<T>
|
ICollection<T> に加えて、インデクサーを使った要素の読み書きができる。
|
IDictionary<TKey, TValue>
|
辞書アクセス(キーを使った値の検索)しての値の読み書きができる。 |
IReadOnlyCollection<T> ※
|
IEnumerable<T> に加えて、要素の個数が取れる。読み取り専用なので共変。
|
IReadOnlyList<T> ※
|
IReadOnlyCollection<T> に加えて、インデクサーを使った要素の読み取りができる。読み取り専用なので共変。
|
IReadOnlyDictionary<TKey, TValue> ※
|
辞書アクセス(キーを使った値の検索)しての値の読み取りができる。 |
Ver. 5.0
※ 読み取り専用系のインターフェイスは .NET Framework 4.5 (C# 5.0と同時期)で追加されました。
このうち、IEnumerable
とIReadIReadOnlyList
の例を挙げておきます。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// 連結リスト。
/// <see cref="IEnumerable{T}"/> を実装している = データの列挙ができる。複数のデータを束ねてる。
/// </summary>
/// <typeparam name="T"></typeparam>
class LinkedList<T> : IEnumerable<T>
{
public T Value { get; }
public LinkedList<T> Next { get; }
public LinkedList(T value) : this(value, null) { }
private LinkedList(T value, LinkedList<T> next) { Value = value; Next = next; }
public LinkedList<T> Add(T value) => new LinkedList<T>(value, this);
public IEnumerator<T> GetEnumerator()
{
if(Next != null)
foreach (var x in Next)
yield return x;
yield return Value;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
class IEnumerableSample
{
public static void Main()
{
var a = new LinkedList<int>(1);
var b = a.Add(2).Add(3).Add(4);
// foreach で使える(これは IEnumerable 必須ではない)
foreach (var x in b)
Console.WriteLine(x);
// string.Join で使える
Console.WriteLine(string.Join(", ", b));
// LINQ で使える
Console.WriteLine(b.Sum());
}
}
using System;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// 4次元上の点。
/// <see cref="IReadOnlyList{T}"/> を実装している = <see cref="IEnumerable{T}"/>に加えて、インデックス指定で値を読める。
/// </summary>
class Point4D : IReadOnlyList<double>
{
public double X { get; }
public double Y { get; }
public double Z { get; }
public double W { get; }
public Point4D(double x, double y, double z, double w) { X = x; Y = y; Z = z; W = w; }
public double this[int index]
{
get
{
switch (index)
{
default:
case 0: return X;
case 1: return Y;
case 2: return Z;
case 3: return W;
}
}
}
public int Count => 4;
public IEnumerator<double> GetEnumerator()
{
yield return X;
yield return Y;
yield return Z;
yield return W;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
class IReadOnlyListSample
{
public static void Main()
{
var p1 = new Point4D(1, 2, 3, 4);
var p2 = new Point4D(3, 7, 5, 11);
// X, Y, Z, W の代わりに 0, 1, 2, 3 のインデックスで値を読み出し
var innerProduct = 0.0;
for (int i = 0; i < 4; i++)
innerProduct += p1[i] * p2[i];
Console.WriteLine(innerProduct);
}
}
IDisposable
IDisposable
インターフェイス(System
名前空間)は、ガベージ コレクション任せではなく、
明示的なタイミングで破棄処理を行いたいものに使います。詳細は「リソースの破棄」で説明します。
using System; /// <summary> /// <see cref="IDisposable"/> を実装している = 使い終わったら明示的に Dispose を呼ぶ必要がある。 /// </summary> class Stopwatch : IDisposable { System.Diagnostics.Stopwatch _s = new System.Diagnostics.Stopwatch(); public Stopwatch() { _s.Start(); } public void Dispose() { _s.Stop(); Console.WriteLine(_s.Elapsed); } } class IDisposableSample { public static void Main() { // using ブロックを抜けたら自動的に Dispose が呼ばれる using (new Stopwatch()) { var t = T(12, 6, 0); } } private static int T(int x, int y, int z) => x <= y ? y : T(T(x - 1, y, z), T(y - 1, z, x), T(z - 1, x, y)); }