基本クラスのメソッドを介してジェネリック型を返すにはどうすればよいですか?
-
03-07-2019 - |
質問
データベースアクセスの規約のいくつかをカプセル化する単純な基本クラスを作成しようとしています。通常、" products_retrieve_product"という名前のsprocを作成します。 productIDに基づいて製品を選択します。メソッド" Retrieve"が欲しい基本クラスで、派生クラスが定義で提供する型を返します。 Genericsで達成しようとしていることは可能ですか?
public class MyBaseClass <T>
{
private string _className;
public MyBaseClass ()
{
_className = this.GetType().Name;
}
public virtual T Retrieve(int ID)
{
Database db = DatabaseFactory.CreateDatabase();
DbCommand dbCommand = db.GetStoredProcCommand(String.Format("{0}s_Retrieve_{0}", _className));
db.AddInParameter(dbCommand, String.Format("@{0}ID", _className), DbType.Int32, ID);
using (IDataReader dr = db.ExecuteReader(dbCommand))
{
if (dr.Read())
{
BOLoader.LoadDataToProps(this, dr);
}
}
return (T)this;
}
}
解決
次のようなことをしたいと思います:
class MyBaseClass<T> where T : MyBaseClass<T>, new()
{
public T Retrieve()
{
return new T();
}
}
class Foo : MyBaseClass<Foo>
{
}
class Program
{
public static void Main()
{
Foo f = new Foo();
Foo f2 = f.Retrieve();
Console.WriteLine(f2.ToString());
}
}
このプログラムを実行すると、Fooのタイプ名がコマンドラインに出力されます。明らかにこれは不自然な例ですが、 MyBaseClass.Retrieve()
でデータベースからロードするときにもっと便利なことができます。
重要なのは、Tに制約を追加して、クラス自体のインスタンスでなければならないようにすることです。これにより、 MyBaseClass&lt; T&gt;をサブクラス化するときに、サブクラスをジェネリック型として指定できます。
これが良いアイデアかどうかはわかりませんが、できるように見えます。
他のヒント
もちろん。あなたの例では、Retrieve(...)が呼び出されたときにFooクラスがBarsを返すようにしたい場合:
public class Foo : MyBaseClass<Bar>{}
うーん、クラス名を取得するためにリフレクションを使用しています。 BOLoaderは、間違いなくリフレクションを使用していくつかのプロパティをロードしています。リフレクションを完全にコミットしないのはなぜですか?
BOLoaderは、この「返品タイプ」のゲームを気にしません。なぜ私たちはすべきですか?
public static class MyBaseClassExtender
{
public static void Retrieve(this MyBaseClass entity, int ID)
{
string className = entity.GetType().Name;
Database db = DatabaseFactory.CreateDatabase();
DbCommand dbCommand = db.GetStoredProcCommand(String.Format("{0}s_Retrieve_{0}", className));
db.AddInParameter(dbCommand, String.Format("@{0}ID", className), DbType.Int32, ID);
using (IDataReader dr = db.ExecuteReader(dbCommand))
{
if (dr.Read())
{
BOLoader.LoadDataToProps(this, dr);
}
}
}
}
その後、あなたは言うだけです:
Foo myFoo = new Foo();
myFoo.Retrieve(2);
いいえ、そうではありません。クラス定義で実際にできることは次のようなものだからです。
public class MyBaseClass<T> : T
現在、ジェネリックでは不可能です。
やらなければならないことは、ファクトリが生成するものからファクトリを分離することです(Tをビルドする別のクラスが必要です。そして、Tで動作するヘルパーメソッド、場合によっては拡張メソッドを提供する必要があります)。