2009-03-18 3 views
14

これは良い考えであるかどうかにかかわらず、実行する関数が呼び出しオブジェクトの型を認識しているインターフェイスを実装することは可能ですか?C#で呼び出しオブジェクトタイプを決定する

class A 
{ 
    private C; 

    public int doC(int input) 
    { 
     return C.DoSomething(input); 
    } 
} 

class B 
{ 
    private C; 

    public int doC(int input) 
    { 
     return C.DoSomething(input); 
    } 
} 

class C 
{ 
    public int DoSomething(int input) 
    { 
     if(GetType(CallingObject) == A) 
     { 
     return input + 1; 
     } 
     else if(GetType(CallingObject) == B) 
     { 
     return input + 2; 
     } 
     else 
     { 
     return input + 3; 
     } 
    } 
} 

これは悪いコーディング習慣であるように(パラメータが変更されませんが、出力があろうから)が、それはさておき、それが可能であることは、私には思えますか?

いくつかの特定の種類の関数を呼び出せるようにしたいのですが、関数へのアクセスを除外できません。 私は

DoSomething(int input, Type callingobject) 

「タイプ」パラメータを持っていることについて考えた。しかしメソッドGetType(B)とは対照的に、呼び出し元のオブジェクトは関係なく、自分のタイプのBを偽装するために、メソッドGetType(これ)を使用するという保証はありません。

これはコールスタックを調べるのと同じように単純な(比較的単純な)ものでしょうか?


EDIT

JaredParの答え(あなたがしたい場合)だけでなく、ジョンFeminellaのを投票してください。私は両方の答えを受け入れることができませんでした。私が以前に考えていなかった解決策をもたらしたJaredの答えとは対照的に、私が求めたものであるため、John Feminellaの答えを受け入れました。

+0

あなたはもう少し背景を与えてもらえますか?なぜ、C.DoSometingAとC.DoSomethingBのような何か別のメソッドを実装するだけではないのです。それ以上は良いですが、A&Bクラスの振る舞いです。 –

+1

短いバージョンでは、1〜2行だけで30〜40個の同じ機能を持つことになります。 – DevinB

+0

@devinb:下記の私の投稿に行った編集を見てください。あなたは別の質問を開くことを検討したいかもしれません。 –

答えて

17

まず、はい、これを行うことは恐ろしい考えであり、すべての種類の堅実な設計原則を破ります。もしそれが開いていれば、単に多態性を使うような別のアプローチを検討してください。これは単一ディスパッチのかなり明確なケースにリファクタリングできるようです。

第2に、可能です。スタックを歩くにはSystem.Diagnostics.StackTraceを使用してください。適切なStackFrameを1レベル上にしてください。次に、StackFrameGetMethod()を使用して、どのメソッドが呼び出し元であるかを判断します。スタックトレースを構築することは潜在的に高価な操作であり、メソッドの呼び出し元が実際にどこから来ているのかを隠す可能性があることに注意してください。


編集: OPからのこのコメントは、それはかなり明確に、これはおそらく、ジェネリックまたは多型方法であることができることになります。 @devinbあなたがしようとしていることの詳細を提供する新しい質問をすることを検討し、良い解決策に役立つかどうかを確認することができます。

ショートバージョンは、私は に終わるであろうということである1行または2行によって単にオフ た30のまたは40に同一の機能を有します。 - devinb(12秒前)

+0

D'oh!私の "重複した答え"を削除する。 。 。ダムの愚かな遅い指。 。 。 +1 BTW –

+1

汗がない - 私はその騎乗したJon Skeetによって常にninja'dになる。ところで、私はあなたの投稿を削除しません。あなたはほとんど私のものに似ているが、何らかの点で違う点を作るなら、間違いなくそれを周りに置いてください。 –

0

あなたが必要とするものを達成するためには、(ほとんど)常に適切なデザインがあります。あなたが実際に何をする必要があるかを説明するために一歩前進するなら、私はあなたがこのようなものに頼る必要がない少なくとも1つの良いデザインを得ることができると確信しています。

+0

私のデザインは非常に緊密に結合され、自己参照の悪夢なので、私はこの特定の醜さを持たないデザインを思いついたが、彼らは自分自身の醜さを持っています。私は各オプションを探そうとしています。 – DevinB

+0

@devinb他のオプションがこれほど醜いことはほとんどありません。 –

+0

もっと複雑です。これとは対照的に、比較的シンプルだが恐ろしい。 = D 私はほとんどが好奇心だった。 – DevinB

13

代替アプローチとして、クラスを要求しているオブジェクトのタイプに基づいて別のクラスを提供することを考えたことがありますか?

public interface IC { 
    int DoSomething(); 
} 

public static CFactory { 
    public IC GetC(Type requestingType) { 
    if (requestingType == typeof(BadType1)) { 
     return new CForBadType1(); 
    } else if (requestingType == typeof(OtherType) { 
     return new CForOtherType(); 
    } 
    ... 
    } 
} 

これは、各メソッドが呼び出し元オブジェクトに基づいて動作を変更するよりもはるかにクリーンなアプローチです。それは、懸念事項をICのさまざまな実装にきれいに分離するでしょう。さらに、それらはすべて実際のCの実装に戻すことができます。いくつかの他の人々はあなたがすぐに関数を呼び出しているものオブジェクトを決定するためにコールスタックを調べることができます指摘したように、コールスタック

を調べる

EDIT。しかし、これはあなたが特別な事件をしたいオブジェクトの1つがあなたに電話しているかどうかを判断する絶対的な方法ではありません。たとえば、私はSomeBadObjectからあなたを呼び出すために以下を行うことができますが、私がそうしたことを判断することは非常に困難です。

public class SomeBadObject { 
    public void CallCIndirectly(C obj) { 
    var ret = Helper.CallDoSomething(c); 
    } 
} 

public static class Helper { 
    public int CallDoSomething(C obj) { 
    return obj.DoSomething(); 
    } 
} 

もちろん、コールスタックをさらに歩くことができます。しかし、それは、別のオブジェクトがDoSomethingを呼び出すときにSomeBadObjectがスタック上にあるための完全に合法的なパスかもしれないので、さらに壊れやすくなります。

+0

これでも、AとBは型をCFactoryに提供する必要がありますか? なりすましを防止するにはどうすればよいですか? – DevinB

+0

@devinb、あなたはスプーフィングを防ぐためにスタックを歩くことができますが、それは決して簡単な方法ではありません。 .Netはそれが呼び出し元であることを知る関数のために設計されたものではなく、結果として、それを動作させるためのあらゆる試みには穴があります。 – JaredPar

+0

悪意のあるコーダーが自分のソースにアクセスできるが、変更できないというパラダイムでコードを作成しようとしています。しかし、私はあなたがすべての "badObject"の可能性を除外することはほぼ不可能だと思います。 – DevinB

0

あなたはスタックトレースを取得してそこから発信者のタイプを判断することができますが、これは私の意見では過剰ですが遅くなります。

A,Bが実装するインターフェイスはどうですか?

interface IFoo { 
    int Value { get; } 
} 

そしてあなたDoSomething方法は次のようになります。

public int DoSomething(int input, IFoo foo) 
    { 
     return input + foo.Value; 
    } 
0

あなたは、スタックトレースを作成するためにSystem.Diagnostics.StackTraceクラスを使用することができます。次に、呼び出し元に関連付けられているStackFrameを検索できます。 StackFrameには、呼び出し元のタイプを取得するために使用できるMethodプロパティがあります。

ただし、上記の方法は、パフォーマンスに重大なコードで使用する必要はありません。

0

コールスタックを調べることはできますが、それは高価で脆弱です。あなたのコードがjit'edされると、コンパイラはあなたのメソッドをインライン展開するかもしれないので、デバッグモードで動作することができる一方、リリースモードでコンパイルされたときには別のスタックを得ることができます。

2

最も簡単な答えは、一般的な送信者のeventargs方法論を使用して、任意のイベントのように送信者オブジェクトを渡すことです。

あなたの呼び出し元のコードは次のようになります。

return c.DoSomething(input, this); 

あなたのdoSomethingの方法は、単にIS演算子を使用してタイプをチェックします:これはもう少しOOPで何かのように思える

public static int DoSomething(int input, object source) 
{ 
    if(source is A) 
     return input + 1; 
    else if(source is B) 
     return input + 2; 
    else 
     throw new ApplicationException(); 

} 

。 Cはメソッドを持つ抽象クラスであり、A、BはCから継承し、単にメソッドを呼び出すことができます。これにより、明らかに偽装されていない基本オブジェクトのタイプを確認することができます。

好奇心で、このコンストラクトで何をしようとしていますか?イベントハンドラのような

+0

おおまかに言えば、CにはAの情報とBの情報が含まれていますが、その情報にアクセスする方法は同じですが、Bは情報にアクセスできません(またはABLEはアクセスできます)。 ...大まかに。 – DevinB

+1

うわー、それは私をさらに混乱させました:Pあなたはそれを現実世界の例にすることができますか?猫と犬の例のように? –

+1

うーん...多分。 データベースにはAmerican SpiesとRussian Spiesのリストが含まれています。このメソッドはリストを返します。アメリカ人が尋ねると、彼らはアメリカのリストを手に入れ、ロシア人が尋ねるとロシア人のリストを手に入れます。 – DevinB

0

構造それは、私は確信してFX警官もあなたが原因ランタイムによってインライン化の可能性がないように確実にこの

static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) 
     { 
      throw new NotImplementedException(); 
     } 
+0

...わかりません。 – DevinB

+0

私はMatt Murrellが提案したものを使用していると言っていますが、少なくともイベントが展開されているのと同じ規約を使用しています。すなわち呼び出し元のオブジェクトを渡してから、あなたの偶数のargs – dfasdljkhfaskldjhfasklhf

2

を行うことをお勧めです。

+0

これらの関数が大きすぎてインライン化できないと仮定します。 – DevinB

0

私は違っこれを処理するだろうが、考えてトラブルになるかもしれません....

は、私はこれを前提としています

クラスAは、情報のクラスEの情報
クラスCの呼び出し用のクラスEに呼び出し、
両方とも同じ方法でE

あなたは3つのクラスをすべて知っていて作成しており、クラスEのコンテンツを一般に公開することはできません。これが当てはまらない場合、コントロールは常に失われます。

論理的に:クラスEのコンテンツを隠すことができる場合、これはDLLを介して配布することによって可能性が高くなります。

あなたはとにかくこれを行う場合は、その理由としても(同じ方法を)クラスAとCの内容を隠したが、AとCはクラスで

BとDの派生クラスのベースとして使用することができませんあなたはクラスEのメソッドを呼び出して結果を渡すメソッドを持っています。このメソッド(派生クラスで使用可能ですが、コンテンツはユーザーに表示されません)では、クラスEのメソッドに渡される「キー」として長い文字列を使用できます。これらの文字列は、どのユーザーによっても推測できず、基底クラスが正しく、私は再び

を考える完璧なOOPの実装だろうEでメソッドをコールする方法の知識をカプセル化したベースクラスA、CおよびE

に...私が見下ろすことができここの複雑さ。

4

Visual Studio 2012(.NET Framework 4.5)から、呼び出し元の属性(VBおよびC#)を使用して、caller informationを自動的にメソッドに渡すことができます。

public void TheCaller() 
{ 
    SomeMethod(); 
} 

public void SomeMethod([CallerMemberName] string memberName = "") 
{ 
    Console.WriteLine(memberName); // ==> "TheCaller" 
} 

呼び出し元の属性は、System.Runtime.CompilerServices名前空間にあります。


これはINotifyPropertyChangedの実装に最適です:

private void OnPropertyChanged([CallerMemberName]string caller = null) { 
    var handler = PropertyChanged; 
    if (handler != null) { 
     handler(this, new PropertyChangedEventArgs(caller)); 
    } 
} 

例:

private string _name; 
public string Name 
{ 
    get { return _name; } 
    set 
    { 
     if (value != _name) { 
      _name = value; 
      OnPropertyChanged(); // Call without the optional parameter! 
     } 
    } 
} 
+0

+1これを指摘するには、4.5の呼び出し元情報属性を追加する目的が実際に必要なコンテキストの素晴らしい例であるため、一見したところで良い設計原則に違反しているように見えるかもしれません。具体的には、トレース、デバッグ、または診断ツールを作成するとき。 – BitMask777

+1

@ BitMask777:はい、別の例では、 'INotifyPropertyChanged'を実装することが理にかなっています。 –

関連する問題