730

私は実装しようとしていますReflectionを使ったデータ変換1私のコードでの例。

GetSourceValue関数にはさまざまな型を比較するスイッチがありますが、これらの型とプロパティを削除して、GetSourceValueパラメータとして単一の文字列のみを使用して、プロパティの値を取得します。クラスとプロパティを文字列で渡して、プロパティの値を解決します。

これは可能ですか?

1 元のブログ記事のWebアーカイブ版

21 답변


1453

 public static object GetPropValue(object src, string propName)
 {
     return src.GetType().GetProperty(propName).GetValue(src, null);
 }

もちろん、検証やその他の機能を追加することをお勧めしますが、それがその要点です。


  • あなたはあなたの質問「私はパスクラスが欲しい」で尋ねました、私はそれが「オブジェクト」を意味したと思います。 " class"ではない「class」以降ほとんど意味がありません。なぜあなたはこれを投票しますか? 「this」を使用するようにこのコードを変更する方法を理解できませんか。代わりに?それはクラスの静的プロパティですか?あなたはより具体的になる必要があります、これは正しい答えです。 - Ed S.
  • src.GetType()。GetProperty(propName).GetValue(src、null)は、ネストしたオブジェクトに対してnullを返します。この場合はSchool.Employee.Nameと同じ意味です。school.GetType()。GetProperty(Employee.Name).GetValue(school、null) - Murali Murugesan
  • @ムラリ:それは間違っているからです。あなたはの値を取得する必要があるでしょうEmployee最初にプロパティを設定してから、もう一度同じことをします。 - Ed S.
  • @ Tomá __Zato:問題ないとのことです。今後の投票を行う前に、自分が何を話しているのかを必ず確認してください。 - Ed S.
  • 素敵でシンプル!ただし、一般的なものにします。public static T GetPropertyValue<T>(object obj, string propName) { return (T)obj.GetType().GetProperty(propName).GetValue(obj, null); } - Ohad Schneider

179

このようなものはどうですか。

public static Object GetPropValue(this Object obj, String name) {
    foreach (String part in name.Split('.')) {
        if (obj == null) { return null; }

        Type type = obj.GetType();
        PropertyInfo info = type.GetProperty(part);
        if (info == null) { return null; }

        obj = info.GetValue(obj, null);
    }
    return obj;
}

public static T GetPropValue<T>(this Object obj, String name) {
    Object retval = GetPropValue(obj, name);
    if (retval == null) { return default(T); }

    // throws InvalidCastException if types are incompatible
    return (T) retval;
}

これにより、次のように単一の文字列を使用してプロパティに降りることができます。

DateTime now = DateTime.Now;
int min = GetPropValue<int>(now, "TimeOfDay.Minutes");
int hrs = now.GetPropValue<int>("TimeOfDay.Hours");

これらのメソッドを静的メソッドまたは拡張として使用することができます。


  • @FredJandがつまずいたことを嬉しく思います!これらの古い記事が現れるといつも驚きます。ちょっと曖昧だったので、それを説明するために少しテキストを追加しました。私はこれらを拡張メソッドとして使うことに切り替え、総称フォームを追加したので、ここに追加しました。 - jheddings
  • なぜヌルガードがforeachの中にいて、上にいないのですか? - Santhos
  • @Santhos、' obj'がforeachループの本体で再定義されている場合は、各反復中にチェックされます。 - jheddings

50

に追加Class

public class Foo
{
    public object this[string propertyName]
    {
        get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
        set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
    }

    public string Bar { get; set; }
}

その後、あなたはとして使用することができます:

Foo f = new Foo();
// Set
f["Bar"] = "asdf";
// Get
string s = (string)f["Bar"];


  • @EduardoCuomo:これでリフレクションを使用することは可能ですか。そのため、クラスにどのようなメンバーが含まれているかを知る必要はありませんか。 - Our Man in Bananas
  • " Bar"があればこれは可能ですか?オブジェクトでしたか? - big_water
  • オブジェクトを文字列キーを持つ辞書のようにしてください。驚くばかり - tim
  • @big_water theSetValueそしてGetValueメソッドはObject。特定の型を扱う必要がある場合は、次の結果をキャストする必要があります。GetValue代入する値をキャストしますSetValue - Eduardo Cuomo
  • @OurManinBananasすみません、あなたの質問を理解できません。何をしたいですか? - Eduardo Cuomo

41

使用についてはどうですかCallByNameMicrosoft.VisualBasic名前空間(Microsoft.VisualBasic.dll)?リフレクションを使用して、通常のオブジェクト、COMオブジェクト、さらには動的オブジェクトのプロパティ、フィールド、およびメソッドを取得します。

using Microsoft.VisualBasic;
using Microsoft.VisualBasic.CompilerServices;

その後

Versioned.CallByName(this, "method/function/prop name", CallType.Get).ToString();


  • 興味深い提案、詳細な検査により、フィールドとプロパティ、COMオブジェクトの両方を処理できることが証明されました。そしてそれは正しく動的バインディングを扱うことさえできます! - IllidanS4
  • これはすべての正しい答えでなければなりません:) - Hoang Minh

25

ヘディングによる素晴らしい答え。 propertyNameがproperty1.property2 [X] .property3になるように、集約された配列またはオブジェクトのコレクションの参照を可能にするように改善したいと思います。

    public static object GetPropertyValue(object srcobj, string propertyName)
    {
        if (srcobj == null)
            return null;

        object obj = srcobj;

        // Split property name to parts (propertyName could be hierarchical, like obj.subobj.subobj.property
        string[] propertyNameParts = propertyName.Split('.');

        foreach (string propertyNamePart in propertyNameParts)
        {
            if (obj == null)    return null;

            // propertyNamePart could contain reference to specific 
            // element (by index) inside a collection
            if (!propertyNamePart.Contains("["))
            {
                PropertyInfo pi = obj.GetType().GetProperty(propertyNamePart);
                if (pi == null) return null;
                obj = pi.GetValue(obj, null);
            }
            else
            {   // propertyNamePart is areference to specific element 
                // (by index) inside a collection
                // like AggregatedCollection[123]
                //   get collection name and element index
                int indexStart = propertyNamePart.IndexOf("[")+1;
                string collectionPropertyName = propertyNamePart.Substring(0, indexStart-1);
                int collectionElementIndex = Int32.Parse(propertyNamePart.Substring(indexStart, propertyNamePart.Length-indexStart-1));
                //   get collection object
                PropertyInfo pi = obj.GetType().GetProperty(collectionPropertyName);
                if (pi == null) return null;
                object unknownCollection = pi.GetValue(obj, null);
                //   try to process the collection as array
                if (unknownCollection.GetType().IsArray)
                {
                    object[] collectionAsArray = unknownCollection as Array[];
                    obj = collectionAsArray[collectionElementIndex];
                }
                else
                {
                    //   try to process the collection as IList
                    System.Collections.IList collectionAsList = unknownCollection as System.Collections.IList;
                    if (collectionAsList != null)
                    {
                        obj = collectionAsList[collectionElementIndex];
                    }
                    else
                    {
                        // ??? Unsupported collection type
                    }
                }
            }
        }

        return obj;
    }


  • MasterList [0] [1]によってアクセスされたListのリストはどうですか? - Jesse Adam

10

からのコードを使用するとエドS.私は

'ReflectionExtensions.GetProperty(Type、string)'はその保護レベルのためにアクセスできません

のようだGetProperty()Xamarin.Formsでは使用できません。TargetFrameworkProfileですProfile7私のポータブルクラスライブラリ(.NET Framework 4.5、Windows 8、ASP.NET Core 1.0、Xamarin.Android、Xamarin.iOS、Xamarin.iOS Classic)で。

今、私は実用的な解決策を見つけました:

using System.Linq;
using System.Reflection;

public static object GetPropValue(object source, string propertyName)
{
    var property = source.GetType().GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, propertyName, StringComparison.OrdinalIgnoreCase));
    return property?.GetValue(source);
}

ソース


  • ほんの小さな可能性のある改善。 IFを置き換えて、次に戻ります。return property?.GetValue(source); - Tomino

7

入れ子のプロパティの議論については、あなたが使用する場合は、すべての反射のものを避けることができますDataBinder.Eval Method (Object, String)以下のように:

var value = DataBinder.Eval(DateTime.Now, "TimeOfDay.Hours");

もちろん、への参照を追加する必要があります。System.Webアセンブリ、しかしこれはおそらく大したことではありません。


6

.NET Standard(1.6以降)で呼び出すメソッドが変更されました。 C#6のnull条件演算子を使うこともできます。

using System.Reflection; 
public static object GetPropValue(object src, string propName)
{
    return src.GetType().GetRuntimeProperty(propName)?.GetValue(src);
}



4

のPropertyInfoを使用するSystem.Reflection名前空間どのプロパティにアクセスしようとしても、Reflectionのコンパイルは問題ありません。実行時にエラーが発生します。

    public static object GetObjProperty(object obj, string property)
    {
        Type t = obj.GetType();
        PropertyInfo p = t.GetProperty("Location");
        Point location = (Point)p.GetValue(obj, null);
        return location;
    }

オブジェクトのLocationプロパティを取得しても問題ありません。

Label1.Text = GetObjProperty(button1, "Location").ToString();

場所を取得します:{X = 71、Y = 27} 同じ方法でlocation.Xまたはlocation.Yを返すこともできます。


4

public static List<KeyValuePair<string, string>> GetProperties(object item) //where T : class
    {
        var result = new List<KeyValuePair<string, string>>();
        if (item != null)
        {
            var type = item.GetType();
            var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var pi in properties)
            {
                var selfValue = type.GetProperty(pi.Name).GetValue(item, null);
                if (selfValue != null)
                {
                    result.Add(new KeyValuePair<string, string>(pi.Name, selfValue.ToString()));
                }
                else
                {
                    result.Add(new KeyValuePair<string, string>(pi.Name, null));
                }
            }
        }
        return result;
    }

これは、List内のすべてのプロパティとその値を取得する方法です。


  • なぜこれをしているのですか?type.GetProperty(pi.Name)それが変数に対して==の場合pi? - weston
  • あなたがC#6.0を使用している場合は、取り除くifそして、やりますselfValue?.ToString()そうでなければ取り除くifそして使うselfValue==null?null:selfValue.ToString() - weston
  • のリストもList<KeyValuePair<変わっている、辞書を使うDictionary<string, string> - weston

2

これは、ネスティングパスを伝えるために文字列を必要としない入れ子のプロパティを見つける別の方法です。シングルプロパティメソッドのEd S.の功績による。

    public static T FindNestedPropertyValue<T, N>(N model, string propName) {
        T retVal = default(T);
        bool found = false;

        PropertyInfo[] properties = typeof(N).GetProperties();

        foreach (PropertyInfo property in properties) {
            var currentProperty = property.GetValue(model, null);

            if (!found) {
                try {
                    retVal = GetPropValue<T>(currentProperty, propName);
                    found = true;
                } catch { }
            }
        }

        if (!found) {
            throw new Exception("Unable to find property: " + propName);
        }

        return retVal;
    }

        public static T GetPropValue<T>(object srcObject, string propName) {
        return (T)srcObject.GetType().GetProperty(propName).GetValue(srcObject, null);
    }


  • 次のことを確認した方が良いでしょう。Type.GetProperty戻るnull呼び出す代わりにGetValueそして持っているNullReferenceExceptionループ内にスローされます。 - Groo

2

あなたがどのオブジェクトを調べているのかについて言及することは決してありません、そしてあなたは与えられたオブジェクトを参照するものを拒絶しているので、私はあなたが静的なものを意味すると思います。

using System.Reflection;
public object GetPropValue(string prop)
{
    int splitPoint = prop.LastIndexOf('.');
    Type type = Assembly.GetEntryAssembly().GetType(prop.Substring(0, splitPoint));
    object obj = null;
    return type.GetProperty(prop.Substring(splitPoint + 1)).GetValue(obj, null);
}

検査対象のオブジェクトをローカル変数でマークしたことに注意してください。objnull静的を意味し、そうでなければあなたが望むものに設定します。また、GetEntryAssembly()「実行中」のアセンブリを取得するためのいくつかの利用可能な方法の1つです。型をロードするのに苦労している場合は、それを試してみることをお勧めします。


2

次のコードは、オブジェクトのインスタンスに含まれるすべてのプロパティ名と値の階層全体を表示するための再帰メソッドです。このメソッドはAlexDの簡易版を使いますGetPropertyValue()このスレッドで上記の答え。このディスカッションスレッドのおかげで、私はこれを行う方法を理解することができました!

たとえば、このメソッドを使用して、すべてのプロパティの爆発またはダンプを表示します。WebService次のようにメソッドを呼び出して応答します。

PropertyValues_byRecursion("Response", response, false);

public static object GetPropertyValue(object srcObj, string propertyName)
{
  if (srcObj == null) 
  {
    return null; 
  }
  PropertyInfo pi = srcObj.GetType().GetProperty(propertyName.Replace("[]", ""));
  if (pi == null)
  {
    return null;
  }
  return pi.GetValue(srcObj);
}

public static void PropertyValues_byRecursion(string parentPath, object parentObj, bool showNullValues)
{
  /// Processes all of the objects contained in the parent object.
  ///   If an object has a Property Value, then the value is written to the Console
  ///   Else if the object is a container, then this method is called recursively
  ///       using the current path and current object as parameters

  // Note:  If you do not want to see null values, set showNullValues = false

  foreach (PropertyInfo pi in parentObj.GetType().GetTypeInfo().GetProperties())
  {
    // Build the current object property's namespace path.  
    // Recursion extends this to be the property's full namespace path.
    string currentPath = parentPath + "." + pi.Name;

    // Get the selected property's value as an object
    object myPropertyValue = GetPropertyValue(parentObj, pi.Name);
    if (myPropertyValue == null)
    {
      // Instance of Property does not exist
      if (showNullValues)
      {
        Console.WriteLine(currentPath + " = null");
        // Note: If you are replacing these Console.Write... methods callback methods,
        //       consider passing DBNull.Value instead of null in any method object parameters.
      }
    }
    else if (myPropertyValue.GetType().IsArray)
    {
      // myPropertyValue is an object instance of an Array of business objects.
      // Initialize an array index variable so we can show NamespacePath[idx] in the results.
      int idx = 0;
      foreach (object business in (Array)myPropertyValue)
      {
        if (business == null)
        {
          // Instance of Property does not exist
          // Not sure if this is possible in this context.
          if (showNullValues)
          {
            Console.WriteLine(currentPath  + "[" + idx.ToString() + "]" + " = null");
          }
        }
        else if (business.GetType().IsArray)
        {
          // myPropertyValue[idx] is another Array!
          // Let recursion process it.
          PropertyValues_byRecursion(currentPath + "[" + idx.ToString() + "]", business, showNullValues);
        }
        else if (business.GetType().IsSealed)
        {
          // Display the Full Property Path and its Value
          Console.WriteLine(currentPath + "[" + idx.ToString() + "] = " + business.ToString());
        }
        else
        {
          // Unsealed Type Properties can contain child objects.
          // Recurse into my property value object to process its properties and child objects.
          PropertyValues_byRecursion(currentPath + "[" + idx.ToString() + "]", business, showNullValues);
        }
        idx++;
      }
    }
    else if (myPropertyValue.GetType().IsSealed)
    {
      // myPropertyValue is a simple value
      Console.WriteLine(currentPath + " = " + myPropertyValue.ToString());
    }
    else
    {
      // Unsealed Type Properties can contain child objects.
      // Recurse into my property value object to process its properties and child objects.
      PropertyValues_byRecursion(currentPath, myPropertyValue, showNullValues);
    }
  }
}


1

Dim NewHandle As YourType = CType(Microsoft.VisualBasic.CallByName(ObjectThatContainsYourVariable, "YourVariableName", CallType), YourType)


2

public static TValue GetFieldValue<TValue>(this object instance, string name)
{
    var type = instance.GetType(); 
    var field = type.GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.FieldType) && e.Name == name);
    return (TValue)field?.GetValue(instance);
}

public static TValue GetPropertyValue<TValue>(this object instance, string name)
{
    var type = instance.GetType();
    var field = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.PropertyType) && e.Name == name);
    return (TValue)field?.GetValue(instance);
}


2

public class YourClass
{
    //Add below line in your class
    public object this[string propertyName] => GetType().GetProperty(propertyName)?.GetValue(this, null);
    public string SampleProperty { get; set; }
}

//And you can get value of any property like this.
var value = YourClass["SampleProperty"];


1

もっと短い方法

var a = new Test { Id = 1 , Name = "A" , date = DateTime.Now};
var b = new Test { Id = 1 , Name = "AXXX", date = DateTime.Now };

var compare = string.Join("",a.GetType().GetProperties().Select(x => x.GetValue(a)).ToArray())==
              string.Join("",b.GetType().GetProperties().Select(x => x.GetValue(b)).ToArray());


1

jheddingsそしてAlexDどちらも、プロパティ文字列の解決方法について優れた回答を書いています。私はまさにその目的のために専用のライブラリを書いたので、私はミックスに私のものを投入したいのです。

Pather.CSharpのメインクラスはResolver。デフォルトでは、プロパティ、配列、辞書のエントリを解決できます。

したがって、たとえば、次のようなオブジェクトがあるとします。

var o = new { Property1 = new { Property2 = "value" } };

そして取得したいProperty2あなたはこのようにすることができます:

IResolver resolver = new Resolver();
var path = "Property1.Property2";
object result = r.Resolve(o, path); 
//=> "value"

これが解決できるパスの最も基本的な例です。他に何ができるのか、またはどのように拡張できるのかを知りたい場合は、単にGithubページ


0

これが私の解決策です。 COMオブジェクトでも動作し、COMオブジェクトからコレクション/配列項目にアクセスすることができます。

public static object GetPropValue(this object obj, string name)
{
    foreach (string part in name.Split('.'))
    {
        if (obj == null) { return null; }

        Type type = obj.GetType();
        if (type.Name == "__ComObject")
        {
            if (part.Contains('['))
            {
                string partWithoundIndex = part;
                int index = ParseIndexFromPropertyName(ref partWithoundIndex);
                obj = Versioned.CallByName(obj, partWithoundIndex, CallType.Get, index);
            }
            else
            {
                obj = Versioned.CallByName(obj, part, CallType.Get);
            }
        }
        else
        {
            PropertyInfo info = type.GetProperty(part);
            if (info == null) { return null; }
            obj = info.GetValue(obj, null);
        }
    }
    return obj;
}

private static int ParseIndexFromPropertyName(ref string name)
{
    int index = -1;
    int s = name.IndexOf('[') + 1;
    int e = name.IndexOf(']');
    if (e < s)
    {
        throw new ArgumentException();
    }
    string tmp = name.Substring(s, e - s);
    index = Convert.ToInt32(tmp);
    name = name.Substring(0, s - 1);
    return index;
}


0

以下の方法は私にとっては完璧に動作します。

class MyClass {
    public string prop1 { set; get; }

    public object this[string propertyName]
    {
        get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
        set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
    }
}

プロパティ値を取得するには

MyClass t1 = new MyClass();
...
string value = t1["prop1].ToString();

プロパティ値を設定するには

t1["prop1] = value;


0

を見てくださいHeleonix.Reflectionとしょうかん。メンバーをパスで取得/設定/呼び出したり、リフレクションより速いgetter / setter(lambdaをデリゲートにコンパイル)を作成することができます。例えば:

var success = Reflector.Get(DateTime.Now, null, "Date.Year", out int value);

または、一度ゲッターを作成し、再利用のためにキャッシュします(これはより高性能ですが、中間メンバーがnullの場合はNullReferenceExceptionをスローする可能性があります)。

var getter = Reflector.CreateGetter<DateTime, int>("Date.Year", typeof(DateTime));
getter(DateTime.Now);

またはあなたが作成したい場合List<Action<object, object>>異なるゲッターの場合は、コンパイル済みデリゲートの基本型を指定するだけです(型変換はコンパイル済みラムダに追加されます)。

var getter = Reflector.CreateGetter<object, object>("Date.Year", typeof(DateTime));
getter(DateTime.Now);


  • 5〜10行で妥当な時間内にあなた自身のコードでそれを実装することができるなら、決してサードパーティのライブラリを使用しないでください。 - Artem G

リンクされた質問


関連する質問

最近の質問