こんにちはー!ニアです。
今回は.NETでクラスや構造体を作る時に、継承して実装すると便利なインターフェースを紹介していきます。
ここでは、数値型の構造体を例として取り上げます。
#1. 数値系の構造体の場合
例えば、System.Int32やSystem.Doubleなどの構造体は、以下の5つのインターフェースを実装しています。
- IComparable
- IFormattable
- IConvertible
- IComparable<T>
- IEquatable<T>
[SerializableAttribute]
[ComVisibleAttribute(true)]
public struct Int32 : IComparable, IFormattable, IConvertible,
IComparable<int>, IEquatable<int>
[SerializableAttribute]
[ComVisibleAttribute(true)]
public struct Double : IComparable, IFormattable, IConvertible,
IComparable<double>, IEquatable<double>
ここからは、その5つのインターフェースの概要と、それらを実装することでできることを紹介していきます。
##1.1. IComparableインターフェース、IComparable<T>インターフェース
###概要
IComparableインターフェースは、2つのインスタンスの大小を比較した結果を返すCompareToメソッドを持ちます。
[ComVisibleAttribute(true)]
public interface IComparable {
int CompareTo( object obj );
}
IComparable<T>インターフェースは、IComparableインターフェースのジェネリクス版です。
[ComVisibleAttribute(true)]
public interface IComparable<in T> {
int CompareTo( T other );
}
###これらのインターフェースを継承して実装することでできること
- 配列やリストのソート(Array.Sort / List<T>.Sort)
- LINQにおける並び替え(Enumerable.OrderBy / Enumerable.OrderByDecending / Enumerable.ThenBy / Enumerable.ThenByDecending)
- ソート済みのコレクション(SortedSet / SortedList(キーで使用) / SortedDictionary(キーで使用))
なお、IComparable.CompareToとIComparable<T>.CompareToの両方を継承した場合、IComparable<T>.CompareToを優先して呼び出されます。
##1.2. IFormattableインターフェース
###概要
IFormattableインターフェースは、書式を指定してインスタンスの値を文字列に変換するToString(String, IFormatProvider)メソッドを持ちます。
[ComVisibleAttribute(true)]
public interface IFormattable {
string ToString( string format, IFormatProvider provider );
}
###このインターフェースを継承して実装することでできること
- 書式を指定して、コンソールやストリームへの書き出し(Console.Write / Console.WriteLineなど)
- String.Formatメソッドで書式を指定
- 書式を指定して、StringBuilderに文字列を追加(StringBuilder.AppendFormat)
- 書式を指定して、文字列挿入(C# 6.0)
##1.3. IConvertibleインターフェース
###概要
IConvertibleインターフェースは、インスタンスの値をプリミティブ型1及びDateTime構造体に変換するメソッドを持ちます。
[ComVisibleAttribute(true)]
public interface IConvertible {
TypeCode GetTypeCode();
bool ToBoolean( IFormatProvider provider );
byte ToByte( IFormatProvider provider );
char ToChar( IFormatProvider provider );
DateTime ToDateTime( IFormatProvider provider );
decimal ToDecimal( IFormatProvider provider );
double ToDouble( IFormatProvider provider );
short ToInt16( IFormatProvider provider );
int ToInt32( IFormatProvider provider );
long ToInt64( IFormatProvider provider );
sbyte ToSByte( IFormatProvider provider );
float ToSingle( IFormatProvider provider );
string ToString( IFormatProvider provider );
object ToType( Type conversionType, IFormatProvider provider );
ushort ToUInt16( IFormatProvider provider );
uint ToUInt32( IFormatProvider provider );
ulong ToUInt64( IFormatProvider provider );
}
実装するメソッドが17個とちょっと多めですね(汗)。
###このインターフェースを継承して実装することでできること
- Convertクラスを利用してプリミティブ型及びDateTime型に変換する
##1.4. IEquatable<T>インターフェース
###概要
IEquatable<T>インターフェースは、2つのインスタンスの値が等しいかどうかを判別するEqualsメソッドを持ちます。
[ComVisibleAttribute(true)]
public interface IEquatable<in T> {
bool Equals( T other );
}
###このインターフェースを継承して実装することでできること
- リストにおける指定した要素の存在の確認したり、インデックスの位置を求めたりする(List<T>.Contains / List<T>.IndexOf)などの処理
- HashSet<T>やDictionary<T>のキーの比較2
#2. インターフェースの実装例
ここでは固定小数点演算を扱う構造体における、インターフェースの実装例を載せます。
using System;
using System.Text.RegularExpressions;
public struct Q10 : IComparable, IFormattable, IConvertible,
IComparable<Q10>, IEquatable<Q10> {
// 固定小数点演算の内部の値
public short Value { get; private set; }
// IComparableの実装
int IComparable.CompareTo( object obj ) {
// objがnull or Q10以外の時
if( obj == null || !( obj is Q10 ) ){
return 1;
}
// 内部の値同士で比較します。
return Value.CompareTo( ( ( Q10 )obj ).Value );
}
// IComparable<Q10>の実装
int IComparable<Q10>.CompareTo( Q10 other ) {
// 内部の値同士で比較します。
return Value.CompareTo( other.Value );
}
// IEquatable<Q10>の実装
bool IEquatable<Q10>.Equals( Q10 other ) {
// 内部の値同士で比較します。
return Value.Equals( other.Value );
}
// IConvertibleの実装(一部のみ抜粋)
double IConvertible.ToDouble( IFormatProvider provider ) {
// 内部の値 / 1024.0 ⇒ 等価な倍精度浮動小数点数に変換します。
return Value / 1024.0;
}
int IConvertible.ToInt32( IFormatProvider provider ) {
// 内部の値 / 1024 ⇒ 等価な符号付き32ビット整数に変換します。
return Value >> 10;
}
static Regex regf = new Regex( @"F\d*?|f\d*?" );
static Regex regd = new Regex( @"ID\d*?|id\d*?" );
// IFormattableの実装
string IFormattable.ToString( string format, IFormatProvider provider ) {
if( format == null ) {
return ( Value / 1024.0 ).ToString();
}
// 書式が「F[数字]」の時は、等価な倍精度浮動小数点数を書式指定して文字列に変換します。
else if( regf.IsMatch( format ) ) {
return ( Value / 1024.0 ).ToString( format );
}
// 書式が「ID[数字]」の時は、内部の値を書式指定して文字列に変換します。
else if( regd.IsMatch( format ) ) {
return Value.ToString( format );
}
throw new FormatException();
}
}
インターフェースを上手に利用して、作成するクラスや構造体をより便利にしてみてはいかがでしょう。
それでは、See you next!