C#ジェネリックとメソッド
質問
どうすれば良い方法を選択できますか(以下の例では、動作しない2つの異なる方法を示しています)。 IFとISでObject型の変数の代わりにジョブを実行していましたが、Objectとboxing / unboxingの使用を避けようとしています。だから、Genericが仕事をすることができると思ったが、私はここで立ち往生している。
ここに私の質問を説明するコードの小さな断片があります:
class Program
{
static void Main(string[] args)
{
Parser p = new Parser();
ObjectType1 o1 = new ObjectType1();
p.execute(o1);
Console.Read();
}
}
class Parser
{
public T execute<T>(T obj)
{
/*
if (obj is ObjectType1)
this.action((ObjectType1)obj);
else if (obj is ObjectType2)
this.action((ObjectType2)obj);
*/
this.action(obj);
return obj;
}
private void action(ObjectType1 objectType1)
{
Console.WriteLine("1");
}
private void action(ObjectType2 objectType2)
{
Console.WriteLine("2");
}
}
class ObjectType1
{
}
class ObjectType2
{
}
更新
インターフェイスとクラスは必要ありません。ごめんなさい。それが質問の目的ではないことは知っていました。
(ObjectType)objでのキャストは機能しませんが、実行した場合:
if (obj is ObjectType1)
this.action(obj as ObjectType1);
else if (obj is ObjectType2)
this.action(obj as ObjectType1);
動作します...なぜ?
そして...すべてのタイプのexecuteメソッドをオーバーロードすることはできません。このメソッドはインターフェースからのものだからです。このため、このメソッドからすべてを呼び出す必要があります。
解決
いいえ、これはできません。ジェネリックはC ++テンプレートのようには機能しません-ジェネリックメソッドは一度だけコンパイルされます。コンパイラがオーバーロード解決に使用できる唯一の情報は、使用するコードに関係なく、ジェネリックメソッド内で知っている情報です。
これを示す例として、期待どおりに動作しない可能性のあるコードを次に示します。
using System;
class Test
{
static void Main()
{
string x = "hello";
string y = string.Copy(x);
Console.WriteLine(x==y); // Overload used
Compare(x, y);
}
static void Compare<T>(T x, T y) where T : class
{
Console.WriteLine(x == y); // Reference comparison
}
}
何をしたいのかを知らなくても先に進む最善の方法を言うのは難しいです。
他のヒント
インターフェースを検討しましたか?
interface IAction
{
void action();
}
class ObjectType1 : IAction
{
void action() {
Console.WriteLine("1");
}
}
class ObjectType2 : IAction
{
void action() {
Console.WriteLine("2");
}
}
class Parser
{
public IAction execute(IAction obj)
{
obj.action();
return obj;
}
}
OPによる編集:
このソリューションでは、すべてのビジネスロジックオブジェクトを変更して、このインターフェイスを使用する必要があります。これは本当にすべきことではありません(私の状況では)。また、他の状況では、ビジネスのものとは関係のないインターフェイスを持たないクリーンなBusinessObjectを常に使用することを好みます。私の質問では、Generic / Object / Delegateメソッドとより関連性の高いソリューションを実現する必要があります。ありがとうございます。この回答は受け入れられません。
クラスパーサーには、オブジェクトタイプに応じて実行メソッドによって呼び出されるプライベートメソッドが多数あります。適切なメソッドにリダイレクトする必要があります。
この作業はコンパイラが行います。オーバーロードを使用してください。
class Parser
{
public ObjectType1 action(ObjectType1 objectType1)
{
Console.WriteLine("1");
return objectType1;
}
public ObjectType2 action(ObjectType2 objectType2)
{
Console.WriteLine("2");
return objectType2;
}
}
class ObjectType1 { }
struct ObjectType2 { }
次に、以下で呼び出されます:
Parser p = new Parser();
p.action(new ObjectType1());
p.action(new ObjectType2());
ボックス化/ボックス化解除はなく、適切なメソッドが呼び出されます。
試したことはありませんが、できますか?
public T execute<T>(T obj)
{
this.action((T)obj);
return obj;
}
(コメントによると、機能しません)
または
public T execute<T>(T obj)
{
this.action(obj as T);
return obj;
}
(コメントによると、作品)
ボクシング/アンボクシングについて心配していることはわかっているので、ここにはValueTypeが関係している可能性があります。
public T execute<T>(T obj)
{
this.action(obj);
return obj;
}
アクションがobjを変更していること、およびその変更が呼び出し側にとって重要であると仮定すること(これが呼び出し側に値を返す理由です)。このコードには、厄介な値渡しの欠陥があります。
このコードを検討してください:
public int execute(int obj)
{
this.action(obj);
return obj;
}
public void action(int obj)
{
obj = obj + 1;
}
この方法で呼び出されます。
int x = p.execute(1);
xは2ではなく1です。
ジェネリックはコンパイル時に発生します。同じコードを異なるタイプに適用する場合に最適です。動的ではないため、入力タイプに応じてメソッドを切り替えるのに役立ちません。
David Bの返信のようにオーバーロード解決は機能しますが、コンパイル時にも発生します。
アップデートのコードも同じことをします。 (型を慎重に確認した後)キャストし、オーバーロードを使用してメソッドを解決します。
ランタイム入力に基づいてメソッドを切り替えたいと思う。
Reflectionを使用すると、より動的な動作を得ることができます。
public object execute(object obj)
{
MethodInfo m = typeof(Parser).GetMethod(
"action",
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new Type[] { obj.GetType() },
null);
m.Invoke(this, new object[] { obj });
return obj;
}
それはおそらく少し壊れやすいですが、例では機能します。
IIRCでは、&quot; where&quot;を使用できます。これを許可する句
public T execute<T>(T obj) where : /* somthing */
{
}
私はいつも自分自身をグーグルにしなければならないので、そのままにしておきます。
編集:コメントを読む。型固有のコードを呼び出すことはお勧めしません。むしろ、そのコードを仮想関数に入れて呼び出します。コール署名は長くなる可能性がありますが、それがオートコンプリートの目的です。