2016-07-18 9 views
0

からの情報を返します。 - EDIT--私が何をしようとしているのかを明確にするために書き換えます。それは簡潔ではないが、文脈は理解に役立つかもしれないという謝罪。リフレクション「GetValue」を使用して、辞書

私はさまざまなシナリオに対して多数のシミュレーションを実行するプログラムを用意しています。これらのシミュレーションとシナリオの結果をクラス(「結果クラス」)に格納し、基本結果をクラスのプロパティとして格納し、さまざまなシナリオと感度をクラス内の辞書に格納します。

異なるユーザーはシミュレーションと異なる出力が必要なので、ユーザーがカスタムレポートを生成できるようにする方法を開発しようとしています。これを行うには、私は反射を使用しています。私はリフレクションを使用してResultsクラスの各Propertyを処理し、Reportクラスと同じ階層を使用してツリービューに追加します。ユーザーは、ツリービューをブラウズし、各プロパティを表示し、レポートで必要なものを選択することができます。例えば::

Results.TotalCapacity; or: 
Results.UsableCapacity; or 
Results.Scenarios[HighCase].TotalCapacity; or 
Results.Sensitivity.ModifiedCapacity. 

関心のあるアイテムにプロパティの枝を説明区切られた文字列 - ユーザーがレポートするプロパティを選択すると

は、私が「パス」にこれを有効にするツリー階層を使用します最終的には、これらのパスを解析するためにリフレクションを使用し、それらのパスから値を取得したいと考えています。

コードをthis linkから大きく借りてしまっていますが、パス内のオブジェクトの1つが辞書の場合、適切なオブジェクトを取得するための最良の方法では苦労しています。

私はこれをうまく機能させることができましたが、どのように改良したり、より堅牢にするかについてのフィードバックをいただければ幸いです。辞書をリストにキャストして、インデックスを介してキーをつかむことは、明らかに最適ではありません。私は、辞書キーを返すために私の 'パス'コードを変更することができますが、リフレクションを使用してキーから辞書の値を取得する方法はわかりません。

コードを次のように

public object GetPropertyValueFromPath(object baseObject, string path) 
{ 
    //Split into the base path elements 
    string[] pp = CorrectPathForDictionaries(path).Split(new[]{ '.' }, StringSplitOptions.RemoveEmptyEntries); 

    //Set initial value of the ValueObject 
    object valueObject = baseObject;    
    foreach (var prop in pp) 
    { 
     if (prop.Contains("[")) 
     { 
      //Will be a dictionary. Get the name of the dictionary, and the index element of interest: 
      string dictionary = prop.Substring(0, prop.IndexOf("[")); 
      int index = Convert.ToInt32(prop.Substring(prop.IndexOf("[") + 1, prop.Length - prop.IndexOf("]"))); 

      //Get the property info for the dictionary 
      PropertyInfo dictInfo = valueObject.GetType().GetProperty(dictionary); 
      if (dictInfo != null) 
      { 
       //Get the dictionary from the PropertyInformation 
       valueObject = dictInfo.GetValue(valueObject, null); 
       //Convert it to a list to provide easy access to the item of interest. The List<> will be a set of key-value pairs. 
       List<object> values = ((IEnumerable)valueObject).Cast<object>().ToList(); 
       //Use "GetValue" with the "value" parameter and the index to get the list object we want. 
       valueObject = values[index].GetType().GetProperty("Value").GetValue(values[index], null); 
      } 
     } 
     else 
     { 
      PropertyInfo propInfo = valueObject.GetType().GetProperty(prop); 
      if (propInfo != null) 
      { 
       valueObject = propInfo.GetValue(valueObject, null); 
      } 
     } 
    } 
    return valueObject; 
} 
+0

by way: "CorrectPathForDictionaries"は単にパスをとり、シナリオ[3] .ModifiedCapacityが 'シナリオ[3] .ModifiedCapacityに変更されるように修正します。 – ainwood

+0

何を試しましたか?試したことを明確に示す良い[mcve]と、あなたが持っている_specific_問題の正確な説明を含めてください。コンパイル時にタイプパラメータが分からないディクショナリでのリフレクションの関連メンバ(もしそうなら、正しいディクショナリタイプにオブジェクトをキャストできます)は 'Item'プロパティです。 –

答えて

0

OK ...世界ではないきれいなコードが、それは(任意の提案が高く評価されている改善するために)動作します。

私は辞書から値を取得しようとしているかどうかを確認するためにテストします。もしそうなら、私は辞書の名前と、要求された要素のインデックスを取得します。私は、PropertyInfoオブジェクトを取得するために辞書名にGetPropertyを使用し、そのPropertyInfoでGetValueを使用して実際の辞書を取得します。キーと値のペアを扱う方法として、辞書をリストにキャストし、次にインデックスを使用して単一のキーと値のペアを取得します。次に、GetProperty( "Value")を呼び出して、辞書から目的のオブジェクトを取得します。あなたが持っているもの、特定の難易度の明確な説明と一緒に、試してみたものを明確に示して良いMinimal, Complete, and Verifiable exampleを欠く

public object GetPropertyValueFromPath(object baseObject, string path) 
    { 
     //Split into the base path elements 
     string[] pp = CorrectPathForDictionaries(path).Split(new[]{ '.' }, StringSplitOptions.RemoveEmptyEntries); 

     //Set initial value of the ValueObject 
     object valueObject = baseObject;    
     foreach (var prop in pp) 
     { 
      if (prop.Contains("[")) 
      { 
       //Will be a dictionary. Get the name of the dictionary, and the index element of interest: 
       string dictionary = prop.Substring(0, prop.IndexOf("[")); 
       int index = Convert.ToInt32(prop.Substring(prop.IndexOf("[") + 1, prop.Length - prop.IndexOf("]"))); 

       //Get the property info for the dictionary 
       PropertyInfo dictInfo = valueObject.GetType().GetProperty(dictionary); 
       if (dictInfo != null) 
       { 
        //Get the dictionary from the PropertyInformation 
        valueObject = dictInfo.GetValue(valueObject, null); 
        //Convert it to a list to provide easy access to the item of interest. The List<> will be a set of key-value pairs. 
        List<object> values = ((IEnumerable)valueObject).Cast<object>().ToList(); 
        //Use "GetValue" with the "value" parameter and the index to get the list object we want. 
        valueObject = values[index].GetType().GetProperty("Value").GetValue(values[index], null); 
       } 
      } 
      else 
      { 
       PropertyInfo propInfo = valueObject.GetType().GetProperty(prop); 
       if (propInfo != null) 
       { 
        valueObject = propInfo.GetValue(valueObject, null); 
       } 
      } 
     } 
     return valueObject; 
    } 
+0

ディクショナリは順序がないので、インデックスで要素を取得すると間違ったことが起こることが保証されます。それがまったく動作すれば、それは純粋に偶然です。 –

+0

これらはツリービューに読み込まれ、同じコードを使用して取得されるため、同じ順序で表示されますが、私はあなたの意見を得ます。 – ainwood

0

、それはあなたがここに求めているものを私に非常に明確ではありません。しかし、と思われるかもしれませんが、コンパイル時にタイプを知らない辞書オブジェクトから特定のキーの値を取得する際に問題があるようです(コンパイル時にタイプを知っていれば、ちょうどその型にキャストし、辞書を直接使用してください)。

それが正しいなら、これは役立つはず:

これを実現するには、2つの方法があります。1つは、クラスのインデクサプロパティの実際にコンパイルされた名前であるItemプロパティを使用して、リフレクションを介してオブジェクトに直接アクセスすることです。それは次のようになります。

private static string GetValue(object dictionary, object key) 
{ 
    PropertyInfo itemProperty = dictionary.GetType().GetProperty("Item"); 

    return itemProperty.GetValue(dictionary, new object[] { key }).ToString(); 
} 

それはストレートフォワード反射です:PropertyInfoオブジェクトを取得し、その値を取得するためにGetValue()方法にkey値を渡します。

これはうまくいきますが、ちょっと面倒なので、値を取得するたびにタイプを調べるためにリフレクションを実行する必要があります。あなたはPropertyInfoオブジェクトをキャッシュすることで後者を改善することができますが、それでもまだ不器用です。

代わりに、同時にコードは、それがdynamicタイプを使用することにより、通常の辞書のルックアップのためにどのように見えるかをはるかに密接に読むことを可能にしながら、ランタイムは、キャッシングの仕事をやらせることができます。

private static string GetValue(dynamic dictionary, dynamic key) 
{ 
    return dictionary[key].ToString(); 
} 

dictionaryオブジェクトとkeyの両方がdynamicである必要があります。この方法でこのようにメソッドを記述すると、実行時に、変数「dynamic」の型とそれらが使用されるコード式に基づいて「バインダ」が作成されます。基本的に、コード式の最終的なコンパイルは、型がわかっているので実行時まで延期され、正常に実行されます。 (異なるタイプの同じステートメントを実行する場合、ランタイムはタイプの組み合わせごとに異なるバインダーを作成します)。

これらのいずれのタイプでも、タイプが指定されていない場合、コンパイル時に知られています。これがあなたの質問に対処できない場合は、MCVEを要望どおりに提供し、そのコードが何をしているのか、あなたが何をしたいのかを正確に説明して質問を改善してください。

関連する問題