| (同じ利用者による、間の8版が非表示) | |||
| 1行目: | 1行目: | ||
== 概要 == | == 概要 == | ||
C# | C#の拡張メソッドは、既存のクラスやインターフェースに新しいメソッドを追加する機能である。<br> | ||
この機能により、元のソースコードを変更せずに、既存の型に機能を追加することができる。<br> | |||
<br> | <br> | ||
また、ソースコードにアクセスできない型やシールされた型に対しても機能を拡張することができる。<br> | |||
<br> | |||
拡張メソッドの特徴を以下に示す。<br> | |||
* 元のクラスを変更せずに新しいメソッドを追加できる。 | |||
* インスタンスメソッドのように呼び出せる。 | |||
* 名前空間を使用して拡張メソッドの可視性を制御できる。 | |||
* 継承やインターフェースの実装よりも柔軟である。 | |||
<br> | |||
ただし、制限事項があることに注意する。<br> | |||
* 拡張メソッドは既存のメソッドをオーバーライドできない。 | |||
* 拡張メソッド内からプライベートメンバにアクセスできない。 | |||
<br> | |||
拡張メソッドの主な用途を以下に示す。<br> | |||
* ユーティリティ関数の追加 | |||
* LINQ (Language Integrated Query) の実装 | |||
* ソースコードの可読性と再利用性の向上 | |||
<br> | |||
<u>※注意</u><br> | |||
<u>過剰な使用は避け、適切な場合にのみ使用すること。</u><br> | |||
<u>名前の衝突に注意が必要である。</u><br> | |||
<br> | |||
<syntaxhighlight lang="c#"> | |||
// 例 : | |||
public static class StringExtensions | |||
{ | |||
public static int WordCount(this string str) | |||
{ | |||
return str.Split(new char[] { ' ', '.', '?' }, | |||
StringSplitOptions.RemoveEmptyEntries).Length; | |||
} | |||
} | |||
// 使用方法 | |||
string text = "Hello, world! How are you?"; | |||
int count = text.WordCount(); // 5 | |||
</syntaxhighlight> | |||
<br><br> | <br><br> | ||
== 拡張メソッドの実装 == | == 拡張メソッドの実装 == | ||
# | # スタティッククラスの作成 | ||
# | #: 拡張メソッドはスタティッククラスの中で定義される。 | ||
# | #: これは、拡張メソッドがどのクラスにも属さないため、スタティッククラスが適しているためである。 | ||
#: また、スタティッククラスは、クライアントコードから参照できる必要がある。 | |||
#: <br> | |||
# スタティックメソッドの作成 | |||
#: 拡張メソッドはスタティックメソッドとして定義される。 | |||
#: 最初のパラメータ (第1引数) の型名の前には、必ず、<code>this</code>キーワードを用いて対象の型を指定する。 | |||
#: これにより、その型のインスタンスでメソッドを呼び出すことが可能になる。 | |||
#: <br> | |||
#: このパラメータは、レシーバーパラメータとも呼ばれ、拡張メソッドがどの型に対して定義されるかを決定する。 | |||
#: <br> | |||
# 拡張メソッドの使用 | |||
#: 拡張メソッドは、通常のインスタンスメソッドと同様に呼び出すことができる。 | |||
#: ただし、異なる名前空間に定義している場合は、<code>using</code>ディレクティブを使用して、拡張メソッドが定義されている名前空間をインポートする必要がある。 | |||
<br> | <br> | ||
もし、拡張メソッド名が既存のインスタンスメソッドと同じ名前の場合、呼び出し元のクラスで定義されているメソッドが優先されるため、<br> | |||
拡張メソッドが使用されることはない。<br> | |||
<br><br> | |||
== 制約 == | |||
==== 制約とは ==== | |||
制約 (Constraints) とは、ジェネリック型パラメータに対して特定の条件を課すものである。<br> | |||
これにより、型の安全性を高め、ソースコード内でその型パラメータの特定の機能を使用できるようになる。<br> | |||
<br> | |||
<u>※注意</u><br> | |||
<u>過度に制約を課すと、ジェネリックの柔軟性が失われる可能性がある。</u><br> | |||
<u>制約は必要最小限にとどめ、コードの再利用性とのバランスを取ることが重要である。</u><br> | |||
<u>一部の制約の組み合わせは論理的に矛盾する場合がある (例: <code>where T : struct, new()</code>)</u><br> | |||
<br> | |||
<u>制約を適切に使用することで、型安全性の高いより表現力豊かなジェネリックコードを記述することができる。</u><br> | |||
<u>ただし、制約の選択には慎重を期し、設計の目的と要件に基づいて適切に判断することが重要である。</u><br> | |||
<br> | |||
==== シンタックス ==== | |||
制約には複数の種類があり、カンマ区切りで複数指定することができる。<br> | |||
<syntaxhighlight lang="c#"> | |||
where <型パラメータ> : <制約1>, <制約2>, <制約3>, ... | |||
</syntaxhighlight> | |||
<br> | |||
==== 主な制約の種類 ==== | |||
* クラス制約 (where T : <クラス名またはサブクラス名>) | |||
*: Tは指定されたクラスまたはそのサブクラスである必要がある。 | |||
*: 例: where T : Animal | |||
*: <br> | |||
* インターフェース制約 (where T : <インターフェース名>) | |||
*: Tは指定されたインターフェースを実装している必要がある。 | |||
*: 例: where T : IComparable<T> | |||
*: <br> | |||
* 構造体 (値型) 制約 (where T : struct) | |||
*: Tは値型である必要がある。 | |||
*: int型、float型、double型、struct型等が該当する。 | |||
*: <br> | |||
* クラス (参照型) 制約 (where T : class) | |||
*: Tは参照型である必要がある。 | |||
*: クラス、インターフェース、デリゲート、配列が該当する。 | |||
*: <br> | |||
* 新しいインスタンス制約 (where T : new()) | |||
*: Tはパラメータ無しのパブリックコンストラクタを持つ必要がある。 | |||
*: <br> | |||
* 非Null制約 (where T : notnull)<br><u>C# 8.0以降</u> | |||
*: Tはnull非許容型である必要がある。 | |||
*: <br> | |||
* アンマネージド制約 (where T : unmanaged) | |||
*: Tはアンマネージド型 (ポインタ、列挙型、構造体等) である必要がある。 | |||
*: <br> | |||
* 他の型パラメータとの関係を示す制約 | |||
*: 例: where T : U | |||
*: ただし、TはUまたはUのサブクラスである必要がある。 | |||
<br> | |||
==== 複数の制約の組み合わせ ==== | |||
複数の制約を組み合わせることにより、より具体的な要件を指定できる。<br> | |||
<br> | |||
以下の例では、Tは参照型、IComparable<T>を実装、パラメータ無しのコンストラクタを持つ必要がある。<br> | |||
<syntaxhighlight lang="c#"> | |||
where T : class, IComparable<T>, new() | |||
</syntaxhighlight> | |||
<br> | |||
==== 制約のメリット ==== | |||
* 型の安全性 | |||
*: コンパイル時に型チェックが行われ、不適切な型の使用を防ぐ。 | |||
* パラメータ特性 | |||
*: 型パラメータの特性が明確になるため、ジェネリッククラスやメソッドでその型の特定の機能を使用できるようになる。 | |||
* IntelliSenseのサポート | |||
*: 制約に基づいて、使用可能なメンバが提案される。 | |||
* パフォーマンスの向上 | |||
*: コンパイラが型情報を活用して最適化を行うことができる。 | |||
* コードの明確性 | |||
*: 型パラメータの要件が明確になり、コードの意図が理解しやすくなる。 | |||
<br> | |||
==== 制約の使用例 ==== | |||
以下の例では、TはIComparable<T>インターフェースを実装して、かつ、パラメータ無しのコンストラクタを持つ必要がある。<br> | |||
<syntaxhighlight lang="c#"> | |||
public class DataProcessor<T> | |||
where T : IComparable<T>, new() | |||
{ | |||
public void ProcessData(T data) | |||
{ | |||
T defaultValue = new T(); // new()制約があるため可能 | |||
if (data.CompareTo(defaultValue) > 0) // IComparable<T>制約があるため可能 | |||
{ | |||
// 何らかの処理 | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
以下の例では、Tは参照型、IComparable<T>インターフェースを実装、パラメータ無しのコンストラクタを持つ必要があり、<br> | |||
また、Uは値型でなければならない。<br> | |||
<syntaxhighlight lang="c#"> | |||
public class GenericClass<T, U> | |||
where T : class, IComparable<T>, new() | |||
where U : struct | |||
{ | |||
// クラスの実装 | |||
} | |||
</syntaxhighlight> | |||
<br><br> | <br><br> | ||
| 93行目: | 242行目: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | |||
ジェネリックを使用して型パラメータを導入する場合、<br> | |||
上記のサンプルコードにおいて、<u>public static T Add<T, U>(this T m, U n)</u>では、2つの型パラメータTとUを使用している。<br> | |||
これにより、メソッドは様々な数値型の組み合わせに対応することができる。<br> | |||
<br> | |||
* 制約 | |||
*: <code>where <型パラメータ> : <制約 1>, <制約 2>, <制約 3>, ...</code> | |||
*: <br> | |||
*: <u>where T : struct, IConvertible</u> | |||
*: <u>where U : struct, IConvertible</u> | |||
*: <br> | |||
*: これらの制約により、TとUは値型 (struct) であり、かつ、IConvertibleインターフェースを実装している必要がある。 | |||
*: これは、ほとんどの数値型 (int, float, double等) に当てはまる。 | |||
*: <br> | |||
* <code>dynamic</code>キーワード | |||
*: dynamic a = m; | |||
*: dynamic b = n; | |||
*: <code>dynamic</code>キーワードを使用することにより、コンパイル時ではなく実行時に型チェックが行われる。 | |||
*: これにより、異なる型同士の加算を可能にしている。 | |||
*: <br> | |||
* 戻り値 | |||
*: return a + b; | |||
*: 動的に型が決定された変数aとbを加算して、その結果を返す。 | |||
*: 戻り値の型は、Tになる。 | |||
<br> | |||
このアプローチのメリットは、1つのメソッドで多くの数値型の組み合わせに対応できることである。<br> | |||
例えば、float型とint型、double型とlong型等の組み合わせでも使用することができる。<br> | |||
<br> | |||
ただし、このアプローチにはいくつかの注意点がある。<br> | |||
* <code>dynamic</code>キーワードの使用により、型安全性が部分的に失われる。 | |||
* パフォーマンスが若干低下する可能性がある。 | |||
* オーバーフローのチェックが行われないため、大きな数値を扱う場合は注意が必要である。 | |||
<br> | <br> | ||
| 140行目: | 321行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
以下の例では、stringクラスに対してUTF-8エンコードされた文字列を返す拡張メソッドを定義している。<br> | |||
<br> | |||
* エンコード処理 | |||
*: Encoding.UTF8.GetBytes(value)を使用して、stringクラスをUTF-8バイト配列に変換する。 | |||
*: Encoding.UTF8.GetString(utf8Bytes)を使用して、そのバイト配列を再びstringクラスに変換する。 | |||
<br> | |||
* エラーハンドリング | |||
*: if (value == null)で、入力された文字列がnullの場合に例外ArgumentNullExceptionをスローする。 | |||
*: これにより、nullに対して拡張メソッドを呼び出した場合のエラーを防ぐことができる。 | |||
<br> | |||
<syntaxhighlight lang="c#"> | |||
// 拡張メソッドの定義 | |||
using System; | |||
using System.Text; | |||
namespace <名前空間> | |||
{ | |||
public static class StringExtensions | |||
{ | |||
// UTF-8エンコードを行う拡張メソッド | |||
public static string ToUtf8String(this string value) | |||
{ | |||
// null チェック | |||
if (value == null) { | |||
throw new ArgumentNullException(nameof(value), "Input string cannot be null."); | |||
} | |||
// UTF-8エンコードを行う | |||
byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); | |||
// バイト配列から再び文字列に変換(UTF-8でデコード) | |||
string utf8String = Encoding.UTF8.GetString(utf8Bytes); | |||
return utf8String; | |||
} | |||
} | |||
} | |||
// 拡張メソッドの使用例 | |||
using System; | |||
using <名前空間>; | |||
class Program | |||
{ | |||
static void Main() | |||
{ | |||
string originalString = "こんにちは、世界!"; | |||
try { | |||
string utf8EncodedString = originalString.ToUtf8String(); | |||
Console.WriteLine(utf8EncodedString); | |||
} | |||
catch (ArgumentNullException ex) { | |||
Console.WriteLine(ex.Message); | |||
} | |||
// 文字列がnullの場合 | |||
string nullString = null; | |||
try { | |||
string result = nullString.ToUtf8String(); // ここで例外がスローされる | |||
} | |||
catch (ArgumentNullException ex) { | |||
Console.WriteLine("Null input detected: " + ex.Message); // ここが呼ばれる | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
<u>※注意</u><br> | |||
<u>UTF-8はバイト列のエンコード方式であり、stringをエンコードして再びstringに変換するという操作は、</u><br> | |||
<u>単に文字列がUTF-8でエンコード可能であることを確認する意味合いに過ぎない場合がある。</u><br> | |||
<u>本当に必要な処理かどうかを確認することが重要である。</u><br> | |||
<br><br> | |||
==== インタフェースおよびジェネリック ==== | ==== インタフェースおよびジェネリック ==== | ||
インタフェースおよびジェネリックに対しても拡張メソッドも作成することができる。<br> | インタフェースおよびジェネリックに対しても拡張メソッドも作成することができる。<br> | ||
| 157行目: | 415行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
==== ジェネリックメソッド ==== | |||
ジェネリックメソッドとは、型パラメータを使用して定義されるメソッドのことである。<br> | |||
これにより、同じロジックを異なる型に対して適用できる柔軟性の高い設計を行うことができる。<br> | |||
<br> | |||
ジェネリックメソッドを使用することにより、型に依存しない汎用的な設計を行うことができ、ソースコードの再利用性と保守性が向上する。<br> | |||
<br> | |||
<u>ただし、ジェネリックメソッドが適切でない場合もあるため、使用する状況を慎重に検討する必要がある。</u><br> | |||
<br> | |||
ジェネリックメソッドのメリットを以下に示す。<br> | |||
* ソースコードの再利用性が向上する。 | |||
* 型安全性を維持しながら、汎用的なソースコードを記述することができる。 | |||
* パフォーマンスが向上する可能性がある。(ボックス化 / アンボックス化の回避) | |||
<br> | |||
ジェネリックメソッドの作成手順を以下に示す。<br> | |||
* メソッド宣言 | |||
*: 通常のメソッド宣言に型パラメータを追加する。 | |||
<syntaxhighlight lang="c#"> | |||
public static T MethodName<T>(T parameter) | |||
</syntaxhighlight> | |||
<br> | |||
* 型パラメータの指定 | |||
*: メソッド名の後にブラケット<code><></code>を使用して、その中に型パラメータを指定する。 | |||
*: <code><T></code> または <code><T, U></code> 等 | |||
<br> | |||
* 必要に応じて制約を追加 | |||
*: 型パラメータに制約を加えるには、<code>where</code>キーワードを使用する。 | |||
<syntaxhighlight lang="c#"> | |||
public static T MethodName<T>(T parameter) where T : struct | |||
</syntaxhighlight> | |||
<br> | |||
* メソッド本体の実装 | |||
*: 型パラメータを使用してメソッドのロジックを実装する。 | |||
<br> | |||
<u>※注意</u><br> | |||
<u>型パラメータは慣習的に大文字のTから始まる。(T, U, V, ....)</u><br> | |||
<u>複雑な制約を設定する場合は、可読性に注意すること。</u><br> | |||
<br> | |||
以下の例では、2つの値を交換するジェネリックメソッドを作成している。<br> | |||
<syntaxhighlight lang="c#"> | |||
public static void Swap<T>(ref T a, ref T b) | |||
{ | |||
T temp = a; | |||
a = b; | |||
b = temp; | |||
} | |||
// 使用例 | |||
int x = 5, | |||
y = 10; | |||
Swap(ref x, ref y); | |||
Console.WriteLine($"x: {x}, y: {y}"); // 出力: x: 10, y: 5 | |||
string s1 = "Hello", | |||
s2 = "World"; | |||
Swap(ref s1, ref s2); | |||
Console.WriteLine($"s1: {s1}, s2: {s2}"); // 出力: s1: World, s2: Hello | |||
</syntaxhighlight> | |||
<br> | |||
==== メソッドチェーン ==== | ==== メソッドチェーン ==== | ||
メソッドチェーンとは、メソッドを鎖のように続けて記述することである。<br> | メソッドチェーンとは、メソッドを鎖のように続けて記述することである。<br> | ||