BaseClass
(またはインターフェイス)から派生したパラメーターを受け入れるメソッドを作成する場合、それを実現する方法は2つあります。
void MyMethod<T>(T obj) where T : BaseClass { ... }
そして
void MyMethod(BaseClass obj) { ... }
どちらを使用した場合の利点/欠点は何ですか?
この例では、2つの間に大きな違いはありません。メソッド内の同じメンバーにアクセスし、同じ派生クラスで呼び出すことができます。ジェネリックメソッドは、呼び出されるタイプごとにコンパイルされるため、実行時の違いがあります。
ジェネリックが役立つのは、T
に応じて値を返す場合です。
ジェネリックを使用すると、次のことができます
T MyMethod<T>(T obj) where T : BaseClass { ... }
MyMethod(derivedInstance).derivedProperty
これがないとエラーになります:
BaseClass MyMethod(BaseClass obj) { ... }
MyMethod(derivedInstance).derivedProperty // error
注基本クラスへの制約について言及しますが、クラスではなくインタフェースに制約する場合、実装が非ジェネリックの構造体である場合、余分なボクシングが発生することに言及する価値がありますバージョンでは、これはパフォーマンスに重大な影響を与える可能性があります。
T
が基本クラスに制約されている場合、すでに述べられているものとは別に実際には大きな違いはありません。
T
がインターフェイスに制限されている場合、違いは大きくなる可能性があります。
int FrobNonGeneric(IFrobbable frob) { //... }
int Frob<T>(T frob) where T: IFrobbable { //... }
struct Frob: IFrobbable { ... }
FrobNonGeneric(new Frob()); //boxing!
Frob(new Frob()); //no boxing
明らかに、引用した例は、他の回答で述べたように、実行時の実行パフォーマンス以外に大きな違いはありません。
ジェネリックコレクションの利点(たとえば、ボクシング/アンボクシングを回避することによるパフォーマンスの向上)を脇に置いて、私たち全員が認識し、頻繁に使用しています-ジェネリックは消費者の観点からも優れた効果を発揮します。たとえば、以下のコードスニペットは、消費者の観点からAPI使用の柔軟性を視覚化するために自明です:
_interface IEntity
{
int Id {get;set;}
}
class Student : IEntity
{
int Id {get;set;}
string SubjectOpted {get;set;}
}
class Employee : IEntity
{
int Id {get;set;}
string DepartmentName{get;set;}
}
interface INonGenericRepository
{
IEntity Get(int id)
}
interface IGenericRepository<T> where T:Entity
{
T Get(int id)
}
class NonGenericRepository : IRepository
{
public IEntity Get(int id) {/*implementation goes here */
}
class GenericRepository<T> : IRepository<T>
{
public T Get(int id) {/*implementation goes here */
}
Class NonGenericStudentConsumer
{
IEntity student = new NonGenericFRepository().Get(5);
var Id = student.Id
var subject = student.SubjectOpted /*does not work, you need to cast */
}
Class GenericStudentConsumer
{
var student = new GenericFRepository<Student>().Get(5);
var Id = student.Id
var subject = student.SubjectOpted /*works perfect and clean */
}
_
制約とともにジェネリックを使用しながら、柔軟性を促進する他のいくつかのユースケースは次のとおりです。
メソッドに渡されるパラメーターがIAdd
とIMultiply
を実装し、IAdd
、IMulitply
の両方を実装するクラスがあることを確認したいとしましょう。
_ public class BusinessOpeartion<T> where T : IAdd, IMultiply{
void SomeBusinessOpeartion(T obj) { /*implementation */}
}
_
一般的ではないアプローチを使用する必要がある場合、次のような冗長なダミーインターフェイスを作成する必要があります。
_interface IDummy : IAdd, IMultiply
public class BusinessOpeartion{
void SomeBusinessOpeartion(IDummy obj) { /*implementation */}
}
_
前者のアプローチはクリーンではありませんか?
また、答えを入力しているときにもう1つ小さなものが表示されます。必要な場合、メソッド内のパラメータータイプの新しいインスタンスをどのように取得しますか:
できません
_IDummy dummy = new IDummy(); /*illegal*/
_
しかし、ジェネリックを使用すると可能性があります。 T temp = new T();
の制約がある場合、new()
また、パラメータタイプのデフォルト値が必要な場合はどうなりますか?
できません
_var default = default(IDummy); /*illegal*/
_
しかし、ジェネリックを使用すると可能性があります。 var default = default(T)
前述のとおり、戻り値を取得した場合にのみ問題になります。これらのケースを考慮してください:
BaseClass MyMethod(BaseClass)
_DervivedClass temp = new DervivedClass();
//Error. My Method always returns a BaseClass. No implicit casting available
temp = MyMethod(temp);
_
これと比較してください:
T MyMethod<T>(T) where T : BaseClass
_DervivedClass temp = new DerivedClass();
temp = MyMethod<DerivedClass>(temp);
_
Strong Typificationは、.NETの親友の1人です。それを受け入れます。それを避けようとしないでください。逆は、PHPおよびJavaScript: http://www.sandraandwoo.com/2015/12/24/0747-melodys-guide-to-プログラミング言語/
質問に含まれている例では、ジェネリックバージョンと非ジェネリックバージョンの間に大きな違いはありません。しかし、ジェネリックなしでは表現できないメソッドシグネチャの他の例を次に示します。
T MyMethod<T>(T obj) where T : BaseClass { ... }
void MyMethod<T>(T obj1, T obj2) where T : BaseClass { ... }
void MyMethod<T>(T obj, List<T> list) where T : BaseClass { ... }