また、拡張メソッドは、C#スタイルで使用したときに、Linqクエリを読みやすくする方法として追加されたことを忘れないでください。
これらの2つの影響は完全に同等ですが、最初の影響ははるかに読みやすくなります(もちろん、連鎖するメソッドが増えると、読みやすさのギャップが大きくなります)。
int n1 = new List<int> {1,2,3}.Where(i => i % 2 != 0).Last();
int n2 = Enumerable.Last(Enumerable.Where(new List<int> {1,2,3}, i => i % 2 != 0));
完全修飾構文は次のようにする必要があることに注意してください。
int n1 = new List<int> {1,2,3}.Where<int>(i => i % 2 != 0).Last<int>();
int n2 = Enumerable.Last<int>(Enumerable.Where<int>(new List<int> {1,2,3}, i => i % 2 != 0));
偶然にも、との型パラメーターはWhere
、Last
これら2つのメソッドの最初のパラメーター(キーワードによって導入され、this
それらを拡張メソッドにするパラメーター)の存在のおかげで推測できるため、明示的に言及する必要はありません。
この点は明らかに(とりわけ)拡張メソッドの利点であり、メソッドチェーンが含まれるすべての同様のシナリオでこの点を利用できます。
特に、基本クラスのメソッドを任意のサブクラスで呼び出して、このサブクラスへの強く型付けされた参照を返すことがわかった、よりエレガントで説得力のある方法です(サブクラスタイプを使用)。
例(わかりました、このシナリオは完全に安っぽいです):おやすみの後、動物は目を開けて泣きます。すべての動物は同じように目を開きますが、犬は吠え、アヒルは鳴きます。
public abstract class Animal
{
}
public static class AnimalExtension
{
public static TAnimal OpenTheEyes<TAnimal>(this TAnimal animal) where TAnimal : Animal
{
return animal;
}
}
public class Dog : Animal
{
public void Bark() { }
}
public class Duck : Animal
{
public void Kwak() { }
}
class Program
{
static void Main(string[] args)
{
Dog Goofy = new Dog();
Duck Donald = new Duck();
Goofy.OpenTheEyes().Bark();
Donald.OpenTheEyes().Kwak();
}
}
概念的にOpenTheEyes
はAnimal
メソッドである必要がありますが、抽象クラスのインスタンスを返しますAnimal
。これは、Bark
またはDuck
などの特定のサブクラスメソッドを認識していません。* 1および* 2としてコメントされた2行は、コンパイルエラーを発生させます。
しかし、拡張メソッドのおかげで、一種の「呼び出されるサブクラスタイプを知っているベースメソッド」を持つことができます。
単純なジェネリックメソッドがその仕事をすることができたかもしれないが、はるかに厄介な方法であることに注意してください:
public abstract class Animal
{
public TAnimal OpenTheEyes<TAnimal>() where TAnimal : Animal
{
return (TAnimal)this;
}
}
今回は、パラメーターがないため、戻り型の推論はできません。呼び出しは他の何物でもありえません:
Goofy.OpenTheEyes<Dog>().Bark();
Donald.OpenTheEyes<Duck>().Kwak();
...より多くの連鎖が含まれる場合、コードの重みが大きくなる可能性があります(特に、typeパラメーターが常に<Dog>
Goofyの行と<Duck>
Donaldの行にあることを知っています...)