2009-08-20 10 views
14

私は、Interopを使用してC#.NETアプリケーションで使用しているCOMインターフェイスをエクスポートするサードパーティのクローズドソースアプリケーションを持っています。このCOMインターフェイスは、適切なインターフェイスタイプにキャストするまで、すべてがSystem.Objectとして表示される多くのオブジェクトをエクスポートします。これらのオブジェクトのすべてのプロパティを割り当てたいと思います。したがって:「プロパティXを持つ」という一般的な関数ですか?

foreach (object x in BigComInterface.Chickens) 
{ 
    (x as Chicken).attribute = value; 
} 
foreach (object x in BigComInterface.Ducks) 
{ 
    (x as Duck).attribute = value; 
} 

しかし、プロパティを割り当てるには、私が回復するので、私は本当に一つ一つの周りのtry/catchを望んでいるから、例外をスローする(不可避であるアプリケーション固有の理由で)可能性があります。したがって:

foreach (object x in BigComInterface.Chickens) 
{ 
    SetAttribute<Chicken>(x as Chicken, value); 
} 
foreach (object x in BigComInterface.Ducks) 
{ 
    SetAttribute<Duck>(x as Duck, value); 
} 

void SetAttribute<T>(T x, System.Object value) 
{ 
    try 
    { 
     x.attribute = value; 
    } 
    catch 
    { 
     // handle... 
    } 
} 

が問題を参照してください:明らか

foreach (object x in BigComInterface.Chickens) 
{ 
    try 
    { 
     (x as Chicken).attribute = value; 
    } 
    catch (Exception ex) 
    { 
     // handle... 
    } 
} 
foreach (object x in BigComInterface.Ducks) 
{ 
    try 
    { 
     (x as Duck).attribute = value; 
    } 
    catch (Exception ex) 
    { 
     // handle... 
    } 
} 

は、それがこれを行うにはそんなにきれいでしょうか?私のxの値はどのタイプでも構わないので、コンパイラは.attributeを解決できません。チキンとアヒルはどんな種類の継承ツリーにもなく、.attributeのインターフェイスを共有していません。もしそうなら、そのインターフェイスの制約をTに置くことができます。しかし、クラスは閉鎖ソースなので、それは私には不可能です。

私が欲しいのは、特定のインターフェイスを実装しているかどうかにかかわらず、に.attributeプロパティがあるという制約が必要なようなものです。ウィットに、

void SetAttribute<T>(T x, System.Object value) where T:hasproperty(attribute) 

は、私はそうでアヒル、牛、羊、および、ペースト/鶏のそれぞれについて、この小さなtry/catchブロックをカットする以外、ここから何をすべきかわかりません。

私の質問は:オブジェクトの特定のプロパティを呼び出すには、そのプロパティを実装するインターフェイスをコンパイル時に知ることができない場合に、この問題を回避するにはどうすればよいですか?

答えて

4

残念ながら、これは現在難しいです。 C#4では、ダイナミックタイプはこれでかなり役に立ちます。 COM interopは、ダイナミックが本当に輝く場所の1つです。

ただし、インターフェイスに制限のない任意のタイプのオブジェクトを持つことができる唯一のオプションは、リフレクションを使用することに戻すことです。

「反射」を使用して「属性」プロパティを検索し、実行時にその値を設定できます。

+0

ありがとう、Reed。私はReflectionテクニックを試しました - それは私が必要とすることについては行いますが、それは遅いです。 – catfood

+0

共通のインターフェースがないので、高速vtableベースの汎用呼び出しを行う方法はありません。名前の動的検索が必要です。これは常に遅く、Reflectionかどうかです。 –

+0

@Pavel:はい、それは常に遅いですが、技術的には、C#4が出るまで、既存のクラスを変更せずに動作する唯一の方法です。私はその良いことを言っているのではなく、むしろそれがうまくいくと言います。 –

2

残念なことに、これを行う唯一の方法は、typeパラメータをそのプロパティを定義するインターフェイスで制約し、すべてのタイプで実装することです。

ソースがないので、実装が不可能なので、何らかの回避策を使用する必要があります。 C#は静的に型付けされているため、ここで使用したい種類のダックタイピングをサポートしていません。すぐに(C#4で)最も良いことは、オブジェクトをdynamicとタイプし、実行時にプロパティ呼び出しを解決することです(このアプローチはgeneric型パラメータをdynamicとして拘束することはできませんので注意してください)。

+3

これはリフレクションで行うことができます。 –

+0

@Reed - 良い点! –

+0

動的では、汎用パラメータは必要ありません。値を動的に取る単一のメソッドを使用して、obj.attributeを設定してみてください。 objのプロパティとして "attribute"が存在すれば正常に動作します。 –

6

さて、あなたの例外処理コードがどのように巨大に応じて、あなたを助けるかもしれない、次のトリックを使用して(私は間違っていないよ場合、それはそれほどすることができる):

class Chicken 
{ 
    public string attribute { get; set; } 
} 

class Duck 
{ 
    public string attribute { get; set; } 
} 

interface IHasAttribute 
{ 
    string attribute { get; set; } 
} 

class ChickenWrapper : IHasAttribute 
{ 
    private Chicken chick = null; 
    public string attribute 
    { 
     get { return chick.attribute; } 
     set { chick.attribute = value; } 
    } 
    public ChickenWrapper(object chick) 
    { 
     this.chick = chick as Chicken; 
    } 
} 

class DuckWrapper : IHasAttribute 
{ 
    private Duck duck = null; 
    public string attribute 
    { 
     get { return duck.attribute; } 
     set { duck.attribute = value; } 
    } 
    public DuckWrapper(object duck) 
    { 
     this.duck = duck as Duck; 
    } 
} 

void SetAttribute<T>(T x, string value) where T : IHasAttribute 
{ 
    try 
    { 
     x.attribute = value; 
    } 
    catch 
    { 
     // handle... 
    } 
} 
+0

これは最終的には最良のソリューションです。ラッパークラスを使用すると、サードパーティのコードを取得し、独自の継承関係などを適用することができます。また、サードパーティのAPIの変更を防ぐことができます。 – plinth

+1

これは一般的には良い解決策ですが、私の最初の問題は、サードパーティ製クローズドソースアプリケーションがSystem.ObjectsでラップされたInterop経由でCOMからChickensとDucksを渡していることです。私はChickenWrappersとDuckWrappersでそれらを包み込むことができますが、そうするためには、利用可能なすべてのオブジェクトを通過させ、Duck、Chicken、Swan、Gooseなどにキャストしようとしてから、結果をラップしますDuckWrapper、ChickenWrapper、SwanWrapper、GooseWrapperなど。 これは恐ろしいswitch文です。 – catfood

+0

実際には、Wrapperのコンストラクタを見てください。オブジェクトを取得してキャストするので、 "Chicken o"の代わりに "new ChickenWrapper(o)"を実行します。 –

2

はDRYに違反していないために原則として反射を使用することができます。本当にジェネリックを使用したい場合は、各タイプのサードパーティーオブジェクトにラッパークラスを使用し、ラッパーにジェネリックタイプの引数を制約するために使用できるインターフェースを実装させることができます。

反射でどのように実行できるかの例。しかし、コードはテストされていません。あなたは、パフォーマンスのためのコードをスピードアップする場合は、あなたがchickenDuckOrWhateverの種類ごとのFieldInfoとPropertyInfoをキャッシュし、反映する前に、最初の辞書を参照してください可能性が

void SetAttribute(object chickenDuckOrWhatever, string attributeName, string value) 
    { 
     var typeOfObject = chickenDuckOrWhatever.GetType(); 
     var property = typeOfObject.GetProperty(attributeName); 
     if (property != null) 
     { 
      property.SetValue(chickenDuckOrWhatever, value); 
      return; 
     } 

     //No property with this name was found, fall back to field 
     var field = typeOfObject.GetField(attributeName); 
     if (field == null) throw new Exception("No property or field was found on type '" + typeOfObject.FullName + "' by the name of '" + attributeName + "'."); 
     field.SetValue(chickenDuckOrWhatever, value); 
    } 

ヒント:attributeNameを文字列としてハードコードする必要がないため、C#6機能nameofを使用できます。例えばnameof(Chicken.AttributeName)と同様です。

+0

そのTIPは優れています。 – catfood

関連する問題