ネストされたオブジェクトのプロパティを取得するためのC#でのリフレクションの使用


82

次のオブジェクトが与えられます:

public class Customer {
    public String Name { get; set; }
    public String Address { get; set; }
}

public class Invoice {
    public String ID { get; set; }
    public DateTime Date { get; set; }
    public Customer BillTo { get; set; }
}

リフレクションを使用してを通過し、InvoiceNameプロパティを取得したいと思いCustomerます。このコードが機能すると仮定して、私が求めているのは次のとおりです。

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);

もちろん、「BillTo.Address」はInvoiceクラスの有効なプロパティではないため、これは失敗します。

そこで、ピリオドで文字列を分割するメソッドを作成し、オブジェクトを歩いて、興味のある最終的な値を探しました。問題なく動作しますが、完全に満足しているわけではありません。

public Object GetPropValue(String name, Object obj) {
    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;
}

この方法を改善する方法、またはこの問題を解決するためのより良い方法に関するアイデアはありますか?

投稿後に編集して、関連する投稿をいくつか見ました...しかし、この質問に具体的に対処する答えはないようです。また、実装に関するフィードバックをお待ちしています。


好奇心が強いのですが、もしあなたGetDesiredInvoiceがあなたにタイプのオブジェクトを返したら、Invoiceなぜinv.BillTo.Name直接使用しないのですか?
ram

私は実際にはこれを少し異なって使用していますが、私の例では単純化しています。オブジェクトを取得して、印刷用のテンプレートとマージするプロセッサに渡します。
jheddings 2009

少し「ブルートフォース」を感じただけで、もっと良い方法があるはずだと思われました。しかし、これまでの回答から、私は完全にオフベースではなかったようです。
jheddings 2009

3
私はこれをすることに夢中だと思いましたが、誰かが私と同じ問題を抱えているようです。ちなみに素晴らしい解決策
Marcello Grechi Lins 2013

:拡張メソッドとしてこれらを使用するための別のトピックに私の返事をまたチェックアウトstackoverflow.com/questions/1196991/...
jheddings

回答:


12

私は実際にあなたの論理は大丈夫だと思います。個人的には、おそらくオブジェクトを最初のパラメーターとして渡すように変更します(これは、PropertyInfo.GetValueとよりインラインであるため、それほど驚くことではありません)。

また、プロパティスタックを検索することを明確にするために、GetNestedPropertyValueのようなものと呼ぶこともあります。


パラメータの並べ替えと提案された名前の変更をお勧めします。
itowlson 2009

フィードバックをありがとう...私は両方の提案を私の実装に取り​​入れました。私はそれをObjectクラスの拡張メソッドに変えることになりました。これにより、パラメーターの並べ替えのポイントが強化されます。
jheddings 2009

なぜ受け入れられた答えですか?OPが要求したように、ネストされたオブジェクトのプロパティを取得するのに役立ちません。
Levitikon 2012年

@Levitikon OPの2番目のコードセットは、求められたことを実行するための適切な方法を示しています。それが私の要点でした。回答自体に記載されているコードに問題はありません。
リードコプシー2012年

27

次のメソッドを使用して、次のような(ネストされたクラス)プロパティから値を取得します

「財産」

"アドレスストリート"

「Address.Country.Name」

    public static object GetPropertyValue(object src, string propName)
    {
        if (src == null) throw new ArgumentException("Value cannot be null.", "src");
        if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");

        if(propName.Contains("."))//complex type nested
        {
            var temp = propName.Split(new char[] { '.' }, 2);
            return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
        }
        else
        {
            var prop = src.GetType().GetProperty(propName);
            return prop != null ? prop.GetValue(src, null) : null;
        }
    }

これがフィドルです:https//dotnetfiddle.net/PvKRH0


プロパティがnullの場合、これは機能しません。作業する前に、最初にsrcがnullであるかどうかを確認する必要があります。
フルティロ2017年

@Furtiroはい、確かに、src(またはpropName)がnullであるかどうかは機能しません。スロー例外を追加しました。ありがとう
DevT 2017年

喜んでお手伝いします!しかし、トリプルネスティングプロパティでは機能しません。2の後に停止します。悲しいですが素晴らしいコードです。
フルティロ2017年

@Furtiroそれは奇妙です、私がちょうど投稿に追加したフィドルで見ることができるように働かなければなりません。見てください、多分あなたはあなたの問題を見つけることができます。
DevT 2017年

@DevTこんにちは、ネストされたクラスを使用するときにGetPropertyを機能させるための同じアプローチがありますか?つまり、var property = type.GetProperty(sortProperty); ネストされたクラスで失敗します(nullの結果が得られるため)、あなたのソリューションはこれに答えることができると思います。(詳細については、ここに記載されているソリューションstackoverflow.com/questions/11336713/…ネストされたクラスで失敗します)
Kynao 2018

13

私はパーティーに少し遅れていることを知っています、そして他の人が言ったように、あなたの実装は問題あり
ません...単純なユースケースのために
ただし、そのユースケースを正確に解決するライブラリPather.CSharpを開発しましたNugetパッケージ
としても利用できます。

その主なクラスはResolverそのResolveメソッドです。
あなたはそれを渡すオブジェクトとプロパティパスを、そしてそれが返されます希望の値を

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Address");

ただし、配列や辞書へのアクセスなど、より複雑なプロパティパスを解決することもできます。
だから、例えば、あなたの場合はCustomer持っていた複数のアドレス

public class Customer {
    public String Name { get; set; }
    public IEnumerable<String> Addresses { get; set; }
}

を使用して2番目のものにアクセスできますAddresses[1]

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Addresses[1]");

2
これは、ネストされたプロパティのnullオブジェクト、つまり、たとえば請求書でNullObjectがnullであるNullObject.Idをどのように処理しますか?
AVFC_Bubble88 2017

10

リフレクションを使用する必要があるACTUALオブジェクトにアクセスする必要があります。これが私が意味することです:

これの代わりに:

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);

これを行う(コメントに基づいて編集):

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo");
Customer cust = (Customer)info.GetValue(inv, null);

PropertyInfo info2 = cust.GetType().GetProperty("Address");
Object val = info2.GetValue(cust, null);

詳細については、この投稿を参照してください: リフレクションを使用してオブジェクトのプロパティのプロパティを設定する


答えてくれてありがとう...私は第1レベルのプロパティの値を取得する方法を知っていますが、ネストされたプロパティを取得する方法を考えていました。実際のアプリケーションでは、実際のオブジェクトにアクセスできません。
jheddings 2009

1
ネストされたプロパティを直接取得する必要があります。また、「請求書」がTで、パス「Property.Property.Property」の文字列がある状況にあります。各プロパティをいじることはできません。
Levitikon 2012年

これは私のためにそれをしました。私もBillTo.Addressで運がなかった。プロパティの設定が同じように機能するかどうか知りたいですか?
yusif_Nurizade 2018

7

パーティーに遅すぎるように聞こえないことを期待して、私は私の解決策を追加したいと思います:この状況では間違いなく再帰を使用してください

public static Object GetPropValue(String name, object obj, Type type)
    {
        var parts = name.Split('.').ToList();
        var currentPart = parts[0];
        PropertyInfo info = type.GetProperty(currentPart);
        if (info == null) { return null; }
        if (name.IndexOf(".") > -1)
        {
            parts.Remove(currentPart);
            return GetPropValue(String.Join(".", parts), info.GetValue(obj, null), info.PropertyType);
        } else
        {
            return info.GetValue(obj, null).ToString();
        }
    }

6

あなたはあなたの「不快感」の原因を説明していませんが、あなたのコードは基本的に私には聞こえます。

私が質問したいのはエラー処理だけです。コードがnull参照をトラバースしようとした場合、またはプロパティ名が存在しない場合は、nullを返します。これにより、エラーが非表示になります。BillToの顧客がいないため、または「BilTo.Address」のスペルを間違えたため、またはBillToの顧客がいて、そのアドレスがnullであるために、nullが返されたかどうかを判断するのは困難です。このような場合は、メソッドをクラッシュさせて書き込みます。例外をエスケープします(または、よりわかりやすいものでラップします)。


3

これは、ネストされたプロパティが列挙子である場合はスキップして、さらに深く続行する別の実装です。文字列型のプロパティは、列挙チェックの影響を受けません。

public static class ReflectionMethods
{
    public static bool IsNonStringEnumerable(this PropertyInfo pi)
    {
        return pi != null && pi.PropertyType.IsNonStringEnumerable();
    }

    public static bool IsNonStringEnumerable(this object instance)
    {
        return instance != null && instance.GetType().IsNonStringEnumerable();
    }

    public static bool IsNonStringEnumerable(this Type type)
    {
        if (type == null || type == typeof(string))
            return false;
        return typeof(IEnumerable).IsAssignableFrom(type);
    }

    public static Object GetPropValue(String name, Object obj)
    {
        foreach (String part in name.Split('.'))
        {
            if (obj == null) { return null; }
            if (obj.IsNonStringEnumerable())
            {
                var toEnumerable = (IEnumerable)obj;
                var iterator = toEnumerable.GetEnumerator();
                if (!iterator.MoveNext())
                {
                    return null;
                }
                obj = iterator.Current;
            }
            Type type = obj.GetType();
            PropertyInfo info = type.GetProperty(part);
            if (info == null) { return null; }

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

この質問とに基づいて

PropertyInfoが Berrylによるコレクションであるかどうかを知る方法

これをMVCプロジェクトで使用して、プロパティを渡して例で並べ替えるだけで、データを動的に並べ替えます

result = result.OrderBy((s) =>
                {
                    return ReflectionMethods.GetPropValue("BookingItems.EventId", s);
                }).ToList();

ここで、BookingItemsはオブジェクトのリストです。


2
> Get Nest properties e.g., Developer.Project.Name
private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName)
            {
                if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0)
                    throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString()));
                if (PropertName.Split('.').Length == 1)
                    return t.GetType().GetProperty(PropertName);
                else
                    return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]);
            }

1
   if (info == null) { /* throw exception instead*/ } 

彼らが存在しないプロパティを要求した場合、私は実際に例外をスローします。GetPropValueを呼び出してnullを返した場合、それがプロパティが存在しなかったのか、プロパティは存在したが値がnullだったのかはわかりません。


さらに、objがnullであるかどうかのチェックをループの外に移動します。
ケビンブロック

申し訳ありませんが、objの繰り返しの使用は見られませんでした。パラメータを変更することは良いプログラミング手法ではありません。これは将来混乱を招く可能性があります。ループ内をトラバースするには、objパラメーターに別の変数を使用します。
ケビンブロック

Kevin:別の変数を使用するには、最後にobjに割り当てるか、メソッドを再帰的にする必要があります。個人的には、これは問題ではないと思います(ただし、良いコメントがあればいいのですが...)
Reed Copsey

1
@Levitikon OPは、「この方法を改善する方法、またはこの問題を解決するためのより良い方法について何かアイデアはありますか?」と述べました。したがって、これは回答であり、コメントではありません。OPが改善を求めたためです。これは改善です。
AaronLS 2012年

1
    public static string GetObjectPropertyValue(object obj, string propertyName)
    {
        bool propertyHasDot = propertyName.IndexOf(".") > -1;
        string firstPartBeforeDot;
        string nextParts = "";

        if (!propertyHasDot)
            firstPartBeforeDot = propertyName.ToLower();
        else
        {
            firstPartBeforeDot = propertyName.Substring(0, propertyName.IndexOf(".")).ToLower();
            nextParts = propertyName.Substring(propertyName.IndexOf(".") + 1);
        }

        foreach (var property in obj.GetType().GetProperties())
            if (property.Name.ToLower() == firstPartBeforeDot)
                if (!propertyHasDot)
                    if (property.GetValue(obj, null) != null)
                        return property.GetValue(obj, null).ToString();
                    else
                        return DefaultValue(property.GetValue(obj, null), propertyName).ToString();
                else
                    return GetObjectPropertyValue(property.GetValue(obj, null), nextParts);
        throw new Exception("Property '" + propertyName.ToString() + "' not found in object '" + obj.ToString() + "'");
    }

1
あなたのソリューションが何に基づいているか、それがOPにどのように役立つかを説明してください。
DontVoteMeDown 2013年

1

手遅れかもしれませんが、自分のソリューションを共有したいと思いました。この解決策は、主にネストされたプロパティが存在するかどうかを確認することです。ただし、必要に応じてプロパティ値を返すように簡単に調整できます。

private static PropertyInfo _GetPropertyInfo(Type type, string propertyName)
        {
            //***
            //*** Check if the property name is a complex nested type
            //***
            if (propertyName.Contains("."))
            {
                //***
                //*** Get the first property name of the complex type
                //***
                var tempPropertyName = propertyName.Split(".", 2);
                //***
                //*** Check if the property exists in the type
                //***
                var prop = _GetPropertyInfo(type, tempPropertyName[0]);
                if (prop != null)
                {
                    //***
                    //*** Drill down to check if the nested property exists in the complex type
                    //***
                    return _GetPropertyInfo(prop.PropertyType, tempPropertyName[1]);
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
            }
        }

この解決策を考え出すには、いくつかの投稿を参照する必要がありました。これは、ネストされた複数のプロパティタイプで機能すると思います。


0

同じ問題を解決する必要があるときにインターネット接続がダウンしたため、「車輪の再発明」を行う必要がありました。

static object GetPropertyValue(Object fromObject, string propertyName)
{
    Type objectType = fromObject.GetType();
    PropertyInfo propInfo = objectType.GetProperty(propertyName);
    if (propInfo == null && propertyName.Contains('.'))
    {
        string firstProp = propertyName.Substring(0, propertyName.IndexOf('.'));
        propInfo = objectType.GetProperty(firstProp);
        if (propInfo == null)//property name is invalid
        {
            throw new ArgumentException(String.Format("Property {0} is not a valid property of {1}.", firstProp, fromObject.GetType().ToString()));
        }
        return GetPropertyValue(propInfo.GetValue(fromObject, null), propertyName.Substring(propertyName.IndexOf('.') + 1));
    }
    else
    {
        return propInfo.GetValue(fromObject, null);
    }
}

すべてがプロパティである限り、ネストの程度に関係なく、これでプロパティ名に使用する文字列の問題が解決されることは間違いありません。


-7

試してみてください inv.GetType().GetProperty("BillTo+Address");

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.