私はC#で次の設計問題プログラミングアプリケーションを持っています。
どちらもCから派生したクラスAとBがあります。それらは外部アセンブリで定義されており、部分的にも定義されていないため、定義を変更できません。
私が達成しようとしているのは、 CオブジェクトがタイプAまたはBである場合、天気に基づいて機能を変えることです。もちろん、提供されたオブジェクトの実行時タイプを比較するifステートメントは使用したくありません。拡張メソッドで可能ですか?私はそうは思わない。解決策はありますか?:)
私はC#で次の設計問題プログラミングアプリケーションを持っています。
どちらもCから派生したクラスAとBがあります。それらは外部アセンブリで定義されており、部分的にも定義されていないため、定義を変更できません。
私が達成しようとしているのは、 CオブジェクトがタイプAまたはBである場合、天気に基づいて機能を変えることです。もちろん、提供されたオブジェクトの実行時タイプを比較するifステートメントは使用したくありません。拡張メソッドで可能ですか?私はそうは思わない。解決策はありますか?:)
ジェネリックスを使用した拡張メソッドで可能になるはずです。複数のアプローチが可能ですが、これが最も簡単です。あなたはそれを得るが
public static void Foo<T>(this T objectC)
where T: C
{
if(typeof(T)==typeof(B){ //or for runtime check: if(objectC is B)
//specific
}
}
次に、AまたはBの任意のインスタンスでFooを呼び出すことができます。
ifステートメントは必要ないとおっしゃっていますが、それを回避しようとしているのはどの程度かわかりません。これを完全に回避する唯一の方法は、A用とB用の2つの拡張メソッドを使用することです(これにより、Cの共通メソッドを呼び出すことができます)が、複数の拡張メソッドを回避しようとしていると思いますか?
編集絶対にifを防ぎたい場合は、Frederikの投稿に示されているように、複数の拡張メソッドを使用する必要があります。基本クラスの拡張機能を追加することもできます。これは、コンパイル中に型が不明な場合にのみ呼び出されます。しかし、それでもifが必要です;)
public static void Foo(this A a)
{
}
public static void Foo(this B b)
{
}
public static void Foo(this C c)
{
if(c is A)
Foo((A)c);
else if(c is B)
Foo((B)c);
else
throw new NotSupportedException(c.GetType().FullName);
}
コンパイル時に型が常にわかっている場合は、AenBの2つの拡張メソッドを使用できます。
私にとって、これは設計上の欠陥のように感じます。実行時型を知らずに機能をオーバーライドおよび変更する2つのサブクラスに共通のクラスを設定できない場合は、全体的な設計に明らかに問題があります。
実行時型によって異なる機能は、実際にそれらのクラスに存在する必要がありますか?
あなたはこれを試すことができます:
public static class CExtensions {
public static void DoIt( this B foo ) {}
public static void DoIt( this A foo ) {}
}
しかし、私はこれがうまくいくとは思いません:
C x = new A();
x.DoIt();
コンパイルされるとは思いませんが、今はテストできません。
この場合、 Decorator パターンを使用できます。
つまり、C から派生した A1、B1 など、独自の 2 つの新しいクラスを作成できます (ただし、C はシール クラスではありません。その場合、解決策は少し異なります)。また、A1 と B1 は、対応するように A と B をラップする必要があります。A1 と B1 のコンストラクターを介して A と B のインスタンスを注入できます。
A1 と B1 の両方が実装する共通のインターフェイスを持つこともできます。インターフェイスには、A1 と B1 が独自の方法で実装する必要があるメソッドが必要です。(A1 と B1 は、必要に応じて通話を A または B に委任できます)
このように、クライアント コードでは、A1 および B1 インスタンスをインターフェイス タイプで参照し、実際の具体的な実装が何であるかを知らなくても、インターフェイスで定義された共通操作を実行できます。
よくわからない場合は、コード サンプルが必要かどうかお知らせください。
このようにできます。必要なさまざまなメソッドを使用してクラスを作成します。同じクラスで、このメソッドのシグネチャを使用してデリゲートを定義し、キーが型で値がデリゲートであるディクショナリを保持します。最後に、メソッド自体を実行しているオブジェクトの型を使用してディクショナリを調べ、正しいメソッドを実行する、クラス C の拡張メソッドを作成できます。このようなもの:
public static class CExtender
{
private static void DoItA(C anA)
{
MessageBox.Show("A");
}
private static void DoItB(C aB)
{
MessageBox.Show("B");
}
private static void DoItC(C aC)
{
MessageBox.Show("C");
}
delegate void DoItDel(C aC);
private static Dictionary<Type, DoItDel> _doItDels;
private static Dictionary<Type, DoItDel> DoItDels
{
get
{
if (_doItDels == null)
{
_doItDels = new Dictionary<Type, DoItDel>();
_doItDels[typeof(A)] = new DoItDel(DoItA);
_doItDels[typeof(B)] = new DoItDel(DoItB);
}
return _doItDels;
}
}
// the only public part is the extension method
public static void DoIt(this C aC)
{
DoItDel aDel;
if (DoItDels.TryGetValue(aC.GetType(), out aDel))
aDel(aC);
else
DoItC(aC);
}
}
純粋に実現可能性の観点から、リフレクションおよび拡張メソッドを使用することは可能です。アプリケーションのコンテキストでそれが意味を成すかどうかは、あなたが判断する必要があります。ここに解決策があります....
class C
{
}
class A : C
{
}
class B : C
{
}
static class Extender
{
public static void M(this B b)
{
Console.WriteLine(" Extension method on B");
}
public static void M(this A a)
{
Console.WriteLine(" Extension method on A");
}
}
static void Main(string[] args)
{
C c = new A();// The actual instance here will be created using some factory.
object instance = Activator.CreateInstance(c.GetType());
Type typeToFind = c.GetType();
Type typeToQuery = typeof(Extender);
var query = from method in typeToQuery.GetMethods(BindingFlags.Static
| BindingFlags.Public | BindingFlags.NonPublic)
where method.IsDefined(typeof(ExtensionAttribute), false)
where method.GetParameters()[0].ParameterType == typeToFind
select method;
// You would be invoking the method based on its name. This is just a quick demo.
foreach (MethodInfo m in query)
{
m.Invoke(instance, new object[] { instance });
}
}