多くの場合、ジェネリック コレクション クラスまたはコレクション内の項目を表すジェネリック クラスのインターフェイスを定義すると便利です。 値型に対するボックス化操作とボックス化解除操作を回避するには、ジェネリック クラスでIComparable<T>を使用することをお勧めします。 .NET クラス ライブラリでは、 System.Collections.Generic 名前空間のコレクション クラスで使用するジェネリック インターフェイスがいくつか定義されています。 これらのインターフェイスの詳細については、「 ジェネリック インターフェイス」を参照してください。
インターフェイスが型パラメーターの制約として指定されている場合は、インターフェイスを実装する型のみを使用できます。 次のコード例は、SortedList<T>
クラスから派生するGenericList<T>
クラスを示しています。 詳細については、「ジェネリックの 概要」を参照してください。
SortedList<T>
は、制約 where T : IComparable<T>
を追加します。 この制約により、BubbleSort
のSortedList<T>
メソッドは、リスト要素でジェネリック CompareTo メソッドを使用できます。 この例では、list 要素は、Person
を実装する単純なクラスIComparable<Person>
です。
//Type parameter T in angle brackets.
public class GenericList<T> : System.Collections.Generic.IEnumerable<T>
{
protected Node head;
protected Node current = null;
// Nested class is also generic on T
protected class Node
{
public Node next;
private T data; //T as private member datatype
public Node(T t) //T used in non-generic constructor
{
next = null;
data = t;
}
public Node Next
{
get { return next; }
set { next = value; }
}
public T Data //T as return type of property
{
get { return data; }
set { data = value; }
}
}
public GenericList() //constructor
{
head = null;
}
public void AddHead(T t) //T as method parameter type
{
Node n = new Node(t);
n.Next = head;
head = n;
}
// Implementation of the iterator
public System.Collections.Generic.IEnumerator<T> GetEnumerator()
{
Node current = head;
while (current != null)
{
yield return current.Data;
current = current.Next;
}
}
// IEnumerable<T> inherits from IEnumerable, therefore this class
// must implement both the generic and non-generic versions of
// GetEnumerator. In most cases, the non-generic method can
// simply call the generic method.
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class SortedList<T> : GenericList<T> where T : System.IComparable<T>
{
// A simple, unoptimized sort algorithm that
// orders list elements from lowest to highest:
public void BubbleSort()
{
if (null == head || null == head.Next)
{
return;
}
bool swapped;
do
{
Node previous = null;
Node current = head;
swapped = false;
while (current.next != null)
{
// Because we need to call this method, the SortedList
// class is constrained on IComparable<T>
if (current.Data.CompareTo(current.next.Data) > 0)
{
Node tmp = current.next;
current.next = current.next.next;
tmp.next = current;
if (previous == null)
{
head = tmp;
}
else
{
previous.next = tmp;
}
previous = tmp;
swapped = true;
}
else
{
previous = current;
current = current.next;
}
}
} while (swapped);
}
}
// A simple class that implements IComparable<T> using itself as the
// type argument. This is a common design pattern in objects that
// are stored in generic lists.
public class Person : System.IComparable<Person>
{
string name;
int age;
public Person(string s, int i)
{
name = s;
age = i;
}
// This will cause list elements to be sorted on age values.
public int CompareTo(Person p)
{
return age - p.age;
}
public override string ToString()
{
return name + ":" + age;
}
// Must implement Equals.
public bool Equals(Person p)
{
return (this.age == p.age);
}
}
public class Program
{
public static void Main()
{
//Declare and instantiate a new generic SortedList class.
//Person is the type argument.
SortedList<Person> list = new SortedList<Person>();
//Create name and age values to initialize Person objects.
string[] names =
[
"Franscoise",
"Bill",
"Li",
"Sandra",
"Gunnar",
"Alok",
"Hiroyuki",
"Maria",
"Alessandro",
"Raul"
];
int[] ages = [45, 19, 28, 23, 18, 9, 108, 72, 30, 35];
//Populate the list.
for (int x = 0; x < 10; x++)
{
list.AddHead(new Person(names[x], ages[x]));
}
//Print out unsorted list.
foreach (Person p in list)
{
System.Console.WriteLine(p.ToString());
}
System.Console.WriteLine("Done with unsorted list");
//Sort the list.
list.BubbleSort();
//Print out sorted list.
foreach (Person p in list)
{
System.Console.WriteLine(p.ToString());
}
System.Console.WriteLine("Done with sorted list");
}
}
次のように、1 つの型に対する制約として複数のインターフェイスを指定できます。
class Stack<T> where T : System.IComparable<T>, IEnumerable<T>
{
}
インターフェイスでは、次のように複数の型パラメーターを定義できます。
interface IDictionary<K, V>
{
}
クラスに適用される継承の規則は、インターフェイスにも適用されます。
interface IMonth<T> { }
interface IJanuary : IMonth<int> { } //No error
interface IFebruary<T> : IMonth<int> { } //No error
interface IMarch<T> : IMonth<T> { } //No error
//interface IApril<T> : IMonth<T, U> {} //Error
ジェネリック インターフェイスが共変である場合、ジェネリック インターフェイスは非ジェネリック インターフェイスから継承できます。つまり、その型パラメーターを戻り値としてのみ使用します。 .NET クラス ライブラリでは、IEnumerable<T>は IEnumerable の戻り値と IEnumerable<T> プロパティ getter 内のT
のみを使用するため、GetEnumeratorはCurrentから継承されます。
具象クラスは、次のように、閉じた構築済みインターフェイスを実装できます。
interface IBaseInterface<T> { }
class SampleClass : IBaseInterface<string> { }
ジェネリック クラスは、次のように、クラス パラメーター リストがインターフェイスに必要なすべての引数を提供する限り、ジェネリック インターフェイスまたは閉じた構築されたインターフェイスを実装できます。
interface IBaseInterface1<T> { }
interface IBaseInterface2<T, U> { }
class SampleClass1<T> : IBaseInterface1<T> { } //No error
class SampleClass2<T> : IBaseInterface2<T, string> { } //No error
メソッドのオーバーロードを制御する規則は、ジェネリック クラス、ジェネリック構造体、またはジェネリック インターフェイス内のメソッドでも同じです。 詳細については、「 ジェネリック メソッド」を参照してください。
C# 11 以降では、インターフェイスは static abstract
または static virtual
メンバーを宣言できます。
static abstract
メンバーまたはstatic virtual
メンバーを宣言するインターフェイスは、ほとんどの場合、ジェネリック インターフェイスです。 コンパイラでは、static virtual
および static abstract
メソッドへの呼び出しをコンパイル時に解決する必要があります。
static virtual
インターフェイスで宣言された static abstract
メソッドには、クラスで宣言された virtual
または abstract
メソッドに似たランタイム ディスパッチ メカニズムがありません。 代わりに、コンパイラでは、コンパイル時に利用できる型情報が使用されます。 通常、これらのメンバーはジェネリック インターフェイスで宣言されます。 さらに、static virtual
または static abstract
メソッドを宣言するほとんどのインターフェイスでは、型パラメーターの 1 つが宣言されたインターフェイスを実装する必要があることが宣言されます。 コンパイラは、指定された型引数を使用して、宣言されたメンバーの型を解決します。
こちらも参照ください
.NET