拡張メソッド:狂気を止める!

むかしむかし、コンピューターが大きくなったとき、プログラマーはひげを生やし、コードの手続き型は、地球上で支配的でした。 プログラマーは、その教義に従うことを考えずに、シンプルで理解可能なコードを書きました。 そして、ドグマはありませんでした。 これらの孤独なカウボーイはそれぞれ、彼自身の世界でクリエーターでした。 それぞれが自分の考えを優雅に、そして力強く表現した。 コードの各行は無制限の賞賛に値する芸術作品でした。 それ以外の方法はありません。コンピューティングリソースが非常に不足していたため、コードを美しく見せるためだけに使用することは考えられませんでした。



しかし、時間が経ち、無敵の技術進歩の機械が鉄の生産性を容赦なく増加させました。 ある晴れた日、人々は最適化されていないコードを書くことができることに気付きました。 計算能力が非常に高まったため、コンピューターは実行する内容を気にしなくなりました。最後の機械命令に対して検証されたコード、またはテストでソーセージのために新入生が書いたコードです。 それはまさに転機となり、その後、雨上がりのキノコのように、新しいプログラミングの概念と技術が現れ始めました。



これらの概念の1つは、オブジェクト指向プログラミングです。 このアイデアは本当に良いことがわかりました。オブジェクトモデルの助けを借りて、システムの機能に関するルールを厳密に記述する明確で秩序立った構造を作成できます。 おそらく、OOPは、ルールの複雑さにより真のメリットをもたらす数少ないパラダイムの1つです。



基本的な例を見てみましょう。OOPが何であるかを思い出してください(つまり、その側面の1つはカプセル化です )。



public class Cat { public float CuddlynessFactor { get; private set; } public Cat(float cuddlynessFactor) { this.CuddlynessFactor = cuddlynessFactor; } public void Purr() { Console.WriteLine("Purr!"); } public void HitTheWall() { Console.WriteLine("Fu**ing meow!!!"); } } public class Dog { public float TeethSharpness { get; private set; }; public Dog(float teethSharpness) { this.TeethSharpness = teethSharpness; } public void Bark() { Console.WriteLine("Bark!"); } public void Bite(Human target) { target.Leg.DoDamage(DamageLevel.Substantial); } }
      
      







ご覧のとおり、最も簡単な2つのクラス、CatとDogを特定しました。 これらの各クラスは、実生活からのオブジェクトの表現であり、システムのコンテキストに関連するこれらのオブジェクトのプロパティをいくつか持っています。 これらのオブジェクトは、何らかの方法で環境(メソッド)と対話でき、いくつかの品質(フィールド)を持ちます。 カプセル化の本質は、各オブジェクトにその機能に必要なすべてのメソッドとデータが含まれていることです。 さらに、オブジェクトには、動作に関係のないメソッドやプロパティが含まれていません(寄木細工の周りを加速し、フルスピードで壁を掘るというアイデアを思いつく犬は何ですか?)。



ここで、これらのオブジェクトに対していくつかのアクションを実行することを想像してください。 たとえば、犬から猫を作りたいです。 この振る舞いは、リストにあるどのクラスにも当てはまりません。なぜなら、彼の右心の犬は©を猫に変身させないからです。 この目的のために、目的の動作を含むサードパーティの静的クラスを作成できます。



 public static class Transmogrificator { public static Cat DogToCat(Dog dog) { return new Cat(Math.Sqrt(dog.TeethSharpness) * 42); } }
      
      







出来上がり! 新しいクラスを使用すると、犬を猫に簡単に変えることができます!



 public void Test() { var uberDog = new Dog(float.Infinity); var testHuman = new Human(); uberDog.Bark(); uberDog.Bite(testHuman); var cat = Transmogrificator.DogToCat(dog); cat.HitTheWall(); }
      
      







甘い! そして現在、Microsoftが.NETで最近導入した拡張メソッドの概念に近づいています。 著者が考えたように、拡張メソッドは、ターゲットクラスに明らかに必要な機能がない(または、少なくともその一部である)場合に適用する必要があります。 拡張メソッドの内容は次のとおりです。 およびy。 MSDN:



一般的に、独自のメソッドを実装するよりもはるかに頻繁に拡張メソッドを呼び出すことになります。

[...]

一般に、拡張メソッドを控えめに実装することをお勧めします。 可能な限り、既存の型を拡張する必要があるクライアントコードは、既存の型から派生した新しい型を作成することで拡張する必要があります。

[...]

拡張メソッドを使用して、ソースコードを変更できない型を拡張する場合、その型の実装の変更により拡張メソッドが破損するリスクがあります。





簡単に言えば、著者は、 本当に必要な場合にのみ拡張メソッドを使用することを勧めています。



次に、 DogToCatメソッドを拡張メソッドにしてサンプルを変更するとどうなるかを見てみましょう。



 public void Test() { var uberDog = new Dog(float.Infinity); var testHuman = new Human(); uberDog.Bark(); uberDog.Bite(testHuman); var cat = dog.DogToCat(); cat.HitTheWall(); }
      
      







ひどいことは何も起こらなかったようです。 しかし、違います! さて、コードを読むとき、完全な印象はDogToCatメソッドがDogタイプの不可欠な部分であるということです。 トリックを理解するには、IntelliSenseプロンプトを待つか、メソッドの即時実装に進む必要があります。 この状況がコーシャーではない理由を見てみましょう。



原則として、通常の静的クラスの代わりに拡張メソッドを使用することに対して、さらに千の引数を考え出すことができます。 しかし、私には、この2つでも叫ぶには十分だと思われます。 考え直してください! 手続き型プログラミングとクラスの混乱の暗黒時代に戻ります! あなたはgognokodに夢中です! 罪人を悔い改めて! Straustrup、Richter、McConellの名前で!」



UPD :どうやら、木に考えを広めたようですが、記事の本質を明確に表現していませんでした。 しかし、一番下の行はこれです: 同じプロジェクト内のクラスの機能を拡張するために拡張メソッドを使用しないことをお勧めします。 ソースにアクセスできないコンパイル済みアセンブリのクラスの拡張機能を記述するのは良いことです。 拡張機能を使用してLINQのような構文を作成すると非常に便利です。 しかし、クラスを作成し、同じプロジェクトで拡張機能を使用して追加機能をハングアップすることは非常に悪いです。



All Articles