2010-11-18 8 views
3

私は、別のソフトウェア用のAPIとインターフェイスするツールを書いています。私のツールの一部には、APIで見つかったさまざまなオブジェクトに関するレポートを生成する必要があります。これらのレポートに、各オブジェクトを識別する単純な文字列を含める必要があります。デフォルトでは、ToString()を使用して各オブジェクトの文字列を生成する予定です。しかし、驚くことではないが、このAPIのデフォルトのToString()実装は説明的ではないことがわかった。パブリックAPIのクラスのToString()をオーバーライドできますか?

最初は、長いSwitchステートメントで以下のコードを実行することを考えていました。これはおそらく管理不能なほど長くなるだろうが。

public string GetAPIObjectDescrition(object obj) 
{ 
    Type t = obj.GetType(); 

    Switch(t) 
    { 
     Case typeof(SomeAPIType): 
      SomeAPIType x = (SomeAPIType)obj; 
      return x.SomeProperty;    


     Case typeof(SomeOtherAPIType): 
      SomeOtherAPITypex = (SomeOtherAPIType)obj; 
      return x.SomeOtherProperty; 

     default: 
      return x.ToString(); 
    } 
} 

次は拡張メソッド(下記のコードを参照)を使用して試しました。 CustomObjectDescription()は期待どおりに動作しましたが、ToString()を呼び出そうとすると、デフォルトのToString()の結果が返されます。私はこれまで拡張メソッドを使用したことがないので、基本的な考え方から完全に外れる可能性もあります。

APIで発生したすべてのタイプのCustomObjectDescription()拡張があるとは限りません。このルートを使用すると、毎回リフレクションを使用して現在のオブジェクトにGetObjectDescription()拡張。可能であれば、反射の使用を避けたいと思います。

public static class APIObjectDescriptionExtensions 
{ 
    public static string ToString(this APIObject element) 
    { 
     return "ElementName = " + element.Name + " ElementID =" + element.Id.IntegerValue.ToString(); 
    } 

    public static string CustomObjectDescription(this APIObject element) 
    { 
     return "ElementName = " + element.Name + " ElementID =" + element.Id.IntegerValue.ToString(); 
    } 
} 

私はこの問題にどのようにアプローチすべきか他に誰かがありますか?私は、各API型のコードが互いに独立しているソリューションを望んでいます(巨大なSwitch文はありません)。

また可能であれば、あるタイプの説明文字列コードが、独自の一意の説明文字列コードを持たない限り、サブタイプに継承することをお勧めします。

カスタムTypeConvertersを作成したり、System.Convert.ToString()をオーバーライド/拡張​​したりするより良い解決策があると思いますか?


更新

私は下の例は、私が何をしようとしている明確に役立つかもしれないと思います。最終的に私はこのAPIから任意のクラスを取ることができるようにしたい。タイプは実行時まで分からず、記述文字列を生成する。 Typeに私のカスタム拡張メソッドがある場合はそれを使用する必要があります。それ以外の場合は、コードを普通の古いToString()に戻す必要があります。

public static string GetDataDescription(object o) 
    { 
     //get the type of the input object 
     Type objectType = o.GetType(); 

     //check to see if a description extension method is defined 
     System.Reflection.MethodInfo extensionMethod = objectType.GetMethod("MyDescriptionExtensionMethod"); 

     if (extensionMethod != null) 
     { 
      //if a description extension method was found returt the result 
      return (string)extensionMethod.Invoke(o, new object[] { }); 
     } 
     else 
     { 
      //otherwise just use ToString(); 
      return o.ToString(); 
     } 
    } 

上記のこのコードは、拡張メソッドaren't found by GetMethod()のでかかわらず動作しません。

+0

拡張メソッドを使用してインスタンスメソッドを非表示にすることはできません。これは、コンパイラがメソッド呼び出し式をバインドしようとするとインスタンスメソッドが最初にチェックされるためです。詳細は、仕様(§7.6.5.1)を参照してください。 – jason

+0

インスタンスメソッドは、同じ名前の拡張メソッドの前に常に使用されます(他のすべてが等しい)。したがってToStringを使用することはできません。 – BrokenGlass

+0

可能性のある複製http://stackoverflow.com/questions/4093501/how-do-i-override-tostring-and-implement-generic?rq=1 –

答えて

0

拡張クラスでToString()以外の別の名前を使用してみましたか?私は拡張メソッドについても完全にはわかっていませんが、私はbase.ToStringがあなたの代わりに呼び出されていると推測しています。おそらく、ToDescription()拡張メソッドを作ると良い結果が得られます。

+0

はい私がOCで述べたようにCustomObjectDescription()、そうです働いた。問題は、すべてのAPIタイプに対してCustomObjectDescription()拡張メソッドがあることを知る方法がないことです。 –

2

あなたはこれに類似したクラスごとにラッパーを提供することができます:

public class SomeAPITypeWrapper : SomeAPIType 
    { 
     public override string ToString() 
     { 
      return SomeProperty; 
     } 
    } 

    public class SomeOtherAPITypeWrapper : SomeOtherAPIType 
    { 
     public override string ToString() 
     { 
      return SomeOtherProperty; 
     } 
    } 

これを確かにあなたの質問に要求されるように基底クラス/サブクラスを使用することができます。また、switchステートメントやヘルパークラスではなく、オブジェクトモデル自体をクリーンに保ちます。

+0

まず、APIクラスが封印されていれば、私は驚くことはありません。そうでない場合、SomeAPItypeWrapperのインスタンスを取得するにはどうすればよいですか? APIはSomeAPITypeインスタンスを返します。だから、本当のAPI型とカスタムラッパークラスの型をマップするための一貫した方法が必要です。 –

+0

ええ、継承の代わりに合成を使用することもできます。 –

+0

これは「ローカル拡張を導入する」ことです。注意事項には、ベースタイプのインスタンスが作成されるたびに作成を制御する(派生型を作成する)必要があります。 2番目:ベースタイプは非シールである必要があります。 – Gishu

0

インスタンスメソッドと拡張メソッドで特定のメソッド呼び出しを解決できる場合、インスタンスメソッドは優先されます。したがって、拡張メソッドは、拡張型のメソッドと同じ名前を持たないように名前を付ける必要があります。

上記のコードから、APIObjectのソースとその派生を制御していないようです。だからあなたのオプションは、「Introduce Foreign Method」と「Introduce Local Extension」であり

  • 私は(C#の拡張メソッドに似ている)外国の方法を試してみた..あなたは、リフレクションをしかし必要になる理由はわかりません。拡張メソッドが存在しない場合は、コンパイル時エラーです。どのようにこの方法を使いますか?
  • 最後にスイッチのステートメントはそれほど長くはありませんが、頻繁に変更する必要はありません。
+0

第3の選択肢があります:Cecilを使用してライブラリにコードを導入します。 – Joshua

+0

@Joshua - wow。これがどのように機能するかはわかりませんが、この同じアセンブリの改ざんはありません。それは強く型付けされたアセンブリやMono/Linux以外のプラットフォームでも動作しますか? – Gishu

+0

私はあなたのツリーからあなたが上書きされているものまで、混在モードのアセンブリがない限り、Microsoftの.NETでも強力な命名を無効にする強力なツールをツールチェストに持っています。 Mono.CecilおよびMono.Securityは、Microsoftの.NET Frameworkに対してコンパイルおよび使用できます。 – Joshua

0

Dictionary<Type,Converter<object,string>>をお勧めします。次にカスタムのストリンジェライザを探すことができ、見つからなければToStringを呼び出します。

辞書は型の完全一致をチェックするので、サブクラスを処理する場合は、基本型が辞書にリストされているかどうかを確認するためのコードを追加する必要があります(ヒント:ベースクラス、またはそうでない場合でも、実際の派生型をディクショナリに追加して、継承ツリーを再帰的に再帰する必要はありません)。あなたはConverter<object,string>契約に準拠Object.ToString()ための「オープンデリゲート」を構築し、デフォルトとしてそれを使用、でも辞書に格納し、代わりにToStringへの呼び出しを特殊ケースの可能

注意。

0

すべてのトーリングをアプリケーションの別の懸念事項にすることができます。 StatePrinter(https://github.com/kbilsted/StatePrinter)は、デフォルトを使用するか、印刷するタイプに応じて構成できるAPIの1つです。

var car = new Car(new SteeringWheel(new FoamGrip("Plastic"))); 
car.Brand = "Toyota"; 

その後、

StatePrinter printer = new StatePrinter(); 
Console.WriteLine(printer.PrintObject(car)); 

それを印刷し、あなたは

new Car() { 
    StereoAmplifiers = null 
    steeringWheel = new SteeringWheel() 
    { 
     Size = 3 
     Grip = new FoamGrip() 
     { 
      Material = ""Plastic"" 
     } 
     Weight = 525 
    } 
    Brand = ""Toyota"" } 

とIValueConverter抽象化であなたはタイプがプリンタであるかを定義することができ、かつFieldHarvesterであなたが定義することができ、次の出力を得ますどのフィールドを文字列に含めるかを指定します。

関連する問題