私がC#で本当に気に入っているのは、ジェネリックリストです。 1つのタイプのオブジェクトのみを含むことができるリスト。 Cocoa/Objective-Cにジェネリックリストのようなものはありますか?私の知る限り、誰が任意のオブジェクトへのポインタを受け取るかはNSArray
しか知りません。
ココアアプリでこれを望んでいることは、しばしば弱いデザインの兆候です。
NSArray
は不変であるため、「任意のオブジェクトへのポインタを取得」せず、渡されたときにおそらくすでに正しいオブジェクトが含まれています。あなたがもっと心配していると思うのは、コードの他の部分が間違った種類のオブジェクトを追加する可能性があると思うNSMutableArray
です。しかし、Cocoa自体を見てください。クラスの設計の一部として可変配列を公開することは非常にまれです。
代わりに、通常、NSArray
とその配列を変更するためのいくつかのメソッドを公開します。次のようなもの:
@class Foo : NSObject
- (NSArray *)bars;
- (void)addBar:(Bar *)bar;
- (void)removeBar:(Bar *)bar;
@end
これは通常、コンパイラの警告を出すだけで間違ったオブジェクトが挿入されるのを防ぎます。もちろん、必要に応じて-addBar:
と-removeBar:
内にアサーションを追加することもできます。
Objective-Cはジェネリックプログラミングをサポートしていません。いつでもObjective-C++とSTLリストを使用できます。
汎用NSArrayは、NSArray
をサブクラス化し、提供されているすべてのメソッドをより制限的なメソッドで再定義することで実現できます。例えば、
- (id)objectAtIndex:(NSUInteger)index
で再定義する必要があります
@interface NSStringArray : NSArray
なので
- (NSString *)objectAtIndex:(NSUInteger)index
nSArrayにNSStringのみを含める場合。
作成されたサブクラスはドロップイン置換として使用でき、コンパイラ警告、プロパティアクセス、より優れたコード作成、Xcodeでの完了などの多くの便利な機能をもたらします。これらはすべてコンパイル時の機能であり、実際の実装を再定義する必要はありません。NSArrayのメソッドは引き続き使用できます。
これを自動化して、2つのステートメントにまとめることができます。これにより、ジェネリックスをサポートする言語に近づけることができます。 WMGenericCollection を使用して自動化を作成しました。ここで、テンプレートはCプリプロセッサマクロとして提供されます。
マクロを含むヘッダーファイルをインポートした後、2つのステートメントを含む汎用NSArrayを作成できます。1つはインターフェイス用、もう1つは実装用です。保存するデータ型とサブクラスの名前を指定するだけで済みます。 WMGenericCollectionは、NSArray
、NSDictionary
、NSSet
、およびそれらの可変の対応するテンプレートを提供します。
いいえ、Objective-Cは現在、コレクション要素のパラメトリックタイピングをサポートしていません。
ただし、このトピックは、質問または既存の回答が認めるよりも複雑です。
パラメトリック-Objective-Cでのコレクションの入力は、C#/ Javaでのジェネリックと同じではありません。たとえば、Objective-Cが、コレクションに追加されたすべてのオブジェクトを保証する機能を追加することはほとんどありません[〜#〜] is [〜#〜] NSArrayタイプまたはサブタイプ。代わりに、Objective-Cはコレクション内のすべてのオブジェクトを保証する機能を持つことができます(そしてIMOはそうすべきです)[〜#〜]準拠[〜#〜]プロトコル/インターフェースに。 (つまり、必要なメソッドのセットを実装していること)
どうして?
Objective-Cは、サブタイプの関係ではなく、プロトコル(インターフェイス)の互換性に基づいて構築された言語です。つまり、オブジェクトがすべての適切なメソッドを持っている場合、オブジェクトは互換性があります。実際のタイプを調べたり、気にしたりすることはありません。実際、実際のタイプを調べることは、Obj-Cでは非常に悪い習慣であり、非常に推奨されていません。この概念は「ダックタイピング」と呼ばれることもあります。これは、アヒルのように鳴くと、アヒルであるためです。文字通り特定のアヒルから受け継いだものかどうかは関係ありません。これにより、他の誰かの実装階層に悩まされるのを防ぐことができます。 -その結果、リストから出てくるオブジェクトにdraw ::メソッドがある限り、それが機能する限り、それが特定のJimmyDrawableBaseオブジェクトのサブクラスであるかどうかは実際には気になりません。
これにより、コードがより再利用可能になるだけでなく、特定の基本クラスから派生したオブジェクトに依存できず、基本クラスが多数あるため、わずかに異なる(より機能的な?)タイプの問題分解が促進されます。それらに強制された実装。
個人的には、Obj-Cコンパイラが[〜#〜] protocol [〜#〜] * CONFORMANCE *のパラメトリックチェックを行うのはNiceだと思います。つまり、配置されたすべてのオブジェクトを必要とするNSMutableArrayを特定のプロトコルに準拠させる(つまり、必要なメソッドの特定のセットを用意する)。
このより柔軟なプロトコル適合性チェックでさえ、動的計画法の人々によって、そして確かな理由で抵抗されることがあります。プログラマーは、適合要件を過剰に指定する方法を持っていることがよくあります。
たとえば、NSArrayプロトコル/インターフェイスに準拠するオブジェクトを含むリストが必要な場合がありますが、[〜#〜]実際には[〜#〜]これらのメソッドのうち2つだけを呼び出す場合があります。これは過度の適合です。互換性のあるアイテムを配列に固定したい人は、実際には呼び出していない大量のメソッドを実装することを余儀なくされます-少なくともまだです(次を参照)。
Google Goは、構造的な互換性を推測することでこの問題を解決しようとします。つまり、リストから出てくるアイテムに対してdraw()を呼び出すと、コンパイラーは、リストに入るすべてのものにdraw()メソッドが含まれていることを確認します。 draw()メソッドが含まれていない場合、リストに入れるのはコンパイラエラーです。これにより、コードが実行時に同じエラーを発生させるのを防ぐことができます。これに伴う問題は、プログラム全体のコンパイルでのみ機能することです。 Google-GoがモジュラーDLLをコンパイルできた場合(それはできません)、リスト内のオブジェクトが3つのメソッドの特定のインターフェイスをサポートする必要があると言う方法がないという問題が発生します。私は将来それらを呼び出すかもしれないなので、今日はそれらを呼び出していません。
これらの2つのソリューションの間では、トレードオフと真実が好きです。
個人的には、Objective-Cにパラメトリックプロトコルの適合性を追加してもらいたいので、特定のコレクションのコンテンツが常に特定のプロトコルのセットに準拠していることを確認するようコンパイラーに依頼できます。
また、コンパイラが過度の適合を回避するのに役立つことを望んでいます。オブジェクトのこれらのプロトコルでメソッドを呼び出さない場合は、エラー/警告が生成されるはずです。それらを使用していないのにプロトコルに保持したい場合は、プロトコル内の各メソッドに対して、「将来使用される可能性があるため、要素はすべて今すぐ提供する必要がある」という宣言を明示的に行う必要があります。 "。これにより、少なくとも、過剰適合のプロセスでは、必要な作業が少ないJava/C#ではなく、より多くの作業が必要になります。