C # 'daki yansımayı kullanarak dizeden özellik değeri alın


928

Benim kod yansıma 1 örnek kullanarak veri dönüşümü uygulamak çalışıyorum .

GetSourceValueFonksiyonu çeşitli karşılaştıran bir anahtarı vardır, ancak ben bu tür ve özellikleri kaldırmak ve olmasını istediğiniz GetSourceValueparametre olarak sadece tek bir dize kullanarak özelliğinin değeri olsun. Dizede bir sınıf ve özellik geçirmek ve özelliğin değerini çözmek istiyorum.

Mümkün mü?

Orijinal blog yazısının 1 Web Arşivi sürümü

Yanıtlar:


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

Tabii ki, doğrulama ve ne eklemek istersiniz, ama bunun özü budur.


8
Güzel ve basit! Yine de genel yapardım:public static T GetPropertyValue<T>(object obj, string propName) { return (T)obj.GetType().GetProperty(propName).GetValue(obj, null); }
Ohad Schneider

2
Bir optimizasyon şu şekilde geçersiz istisna riskini ortadan kaldırabilir: " src.GetType().GetProperty(propName)?.GetValue(src, null);";).
shA.t

8
@ shA.t: Bence bu kötü bir fikir. Mevcut bir mülkün null değeri arasında veya hiç mülk yokken nasıl ayrım yaparsınız? Kötü bir mülk adı gönderdiğimi hemen bilmek isterim. Bu üretim kodu değildir, ancak daha özel bir istisna atmak için daha iyi bir gelişme olacaktır (örneğin null açık GetPropertyve atma PropertyNotFoundExceptionveya null ise bir şey olup olmadığını kontrol edin )
Ed S.

210

Böyle bir şeye ne dersin:

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;
}

Bu, aşağıdaki gibi tek bir dize kullanarak özelliklere inmenize olanak tanır:

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

Bu yöntemleri statik yöntemler veya uzantılar olarak kullanabilirsiniz.


3
@FredJand tökezlediğine sevindim! Bu eski yazıların ortaya çıkması her zaman şaşırtıcıdır. Biraz belirsizdi, bu yüzden açıklamak için biraz metin ekledim. Ayrıca bunları uzantı yöntemleri olarak kullanmaya başladım ve bir jenerik formu ekledim, bu yüzden buraya ekledim.
jheddings

Boş koruma neden öngörünümde ve üstte değil?
Santhos

4
@Santhos, foreach döngüsünün gövdesinde 'obj' yeniden tanımlandığından, her yineleme sırasında kontrol edilir.
jheddings

Yararlı, ancak kenar durumunda yuvalanmış özelliklerden birinin gizlenebilmesi ('yeni' değiştirici kullanılarak), yinelenen özellikleri bulmak için bir istisna atar. Tıpkı yuvalanmış bir özellikteki özelliğe erişmek gibi , son özellik türünü izlemek ve yuvalanmış özelliklerin PropertyInfo.PropertyTypeyerine kullanmak daha düzenli obj.GetType()olur.
Nullius

Sihirli dizelerden kurtulmak ve bu çağrılara derleme zamanı güvenliği eklemek için işlevi çağırırken, name parametresinde nameofC # 6'dan itibaren ifadeyi kullanabilirsiniz nameof(TimeOfDay.Minutes).
Siem

74

Herhangi birine ekleyin 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; }
}

Ardından şu şekilde kullanabilirsiniz:

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

@EduardoCuomo: Sınıfta hangi üyelerin olduğunu bilmenize gerek kalmaması için bununla yansıma kullanmak mümkün mü?
Adamımız

"Bar" bir nesne olsaydı bunu yapmak mümkün mü?
big_water

@big_water SetValueve GetValueyöntemleri ile çalışır Object. Belirli bir türle çalışmanız gerekiyorsa, sonucunu GetValueatamanız ve atamak için değeri kullanmanız gerekirSetValue
Eduardo Cuomo

Üzgünüm @ManinBananas, sorunuzu anlayamıyorum. Ne yapmak istiyorsun?
Eduardo Cuomo

Bu tür yöntemlerin adı nedir ..?
Sahan Chinthaka

45

İsim CallByNamealanının Microsoft.VisualBasic( Microsoft.VisualBasic.dll) kullanılmasına ne dersiniz ? Normal nesnelerin, COM nesnelerinin ve hatta dinamik nesnelerin özelliklerini, alanlarını ve yöntemlerini almak için yansıma kullanır.

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

ve sonra

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

5
İlginç öneri, daha fazla inceleme, hem alanları hem de özellikleri, COM nesnelerini ve hatta doğru dinamik bağlamayı işleyebildiğini kanıtladı !
IllidanS4

Bir hata alıyorum: 'MyType' türünde genel üye 'MyPropertyName' bulunamadı.
vldmrrdjcc

30

Jheddings tarafından büyük cevap. Böylece, özellik dizisi özellik1.property2 [X] .property3 olabilir böylece toplu diziler veya nesne koleksiyonları başvuru izin vererek geliştirmek istiyorum:

    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 object[];
                    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] tarafından erişilen Liste Listesi ne olacak?
Jesse Adam

as Array -> as object [] de Nullreference istisnasına neden olur. Benim için işe yarayan (en etkili yöntem değil prop), bilinmeyen toplamayı IEnumerable'a dökmek ve sonuçta ToArray () kullanmaktır. keman
Jeroen Jonkman

14

Ben kodu kullanırsanız Ed S. I get

'ReflectionExtensions.GetProperty (Type, string)' koruma seviyesi nedeniyle erişilemiyor

Görünüşe göre GetProperty()Xamarin.Forms'da mevcut değil. TargetFrameworkProfileolan Profile7benim Taşınabilir Sınıf Kitaplığı (.NET Framework 4.5, Windows 8, ASP.NET Çekirdek 1.0, Xamarin.Android, Xamarin.iOS, Xamarin.iOS Klasik) içinde.

Şimdi çalışan bir çözüm buldum:

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);
}

Kaynak


4
Sadece küçük bir gelişme. IF ve sonraki dönüşü değiştirin: return property? .GetValue (source);
Tomino

11

Yuvalanmış özellikler tartışması hakkında, DataBinder.Eval Method (Object, String)aşağıdaki gibi kullanırsanız tüm yansıma öğelerinden kaçınabilirsiniz :

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

Tabii ki, System.Webmeclise bir referans eklemeniz gerekecek , ancak bu muhtemelen önemli değil.


8

Arama yöntemi .NET Standardında (1.6 itibariyle) değişmiştir. Ayrıca C # 6'nın boş koşullu operatörünü kullanabiliriz.

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

1
yukarı kullanmak için? operator
blfuentes

4

System.Reflection ad alanının PropertyInfo'yu kullanma . Yansıma, hangi özelliğe erişmeye çalıştığımız önemli değil. Hata, çalışma zamanı sırasında ortaya çıkacaktır.

    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;
    }

Bir nesnenin Location özelliğini almak iyi çalışır

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

Konumu alacağız: {X = 71, Y = 27} Ayrıca location.X veya location.Y'yi de aynı şekilde döndürebiliriz.


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;
    }

Bu, Listedeki değerleriyle tüm özellikleri almanın bir yoludur.


Bunu neden yapıyorsunuz: type.GetProperty(pi.Name)değişkene == ne zaman pi?
weston

C # 6.0 kullanıyorsanız, kurtulun ifve yapın selfValue?.ToString()Aksi takdirde kurtulun ifve kullanınselfValue==null?null:selfValue.ToString()
weston

Ayrıca List<KeyValuePair<tuhaf bir listesi, bir sözlük kullanınDictionary<string, string>
weston

3

Aşağıdaki kod, bir nesnenin örneğinde bulunan tüm Özellik Adlarının ve Değerlerinin tüm hiyerarşisini görüntülemek için Özyinelemeli bir yöntemdir. Bu yöntem, GetPropertyValue()bu iş parçacığında AlexD'nin cevabının basitleştirilmiş bir sürümünü kullanır . Bu tartışma dizisi sayesinde, bunun nasıl yapılacağını anlayabildim!

Örneğin WebService, yöntemi aşağıdaki gibi çağırarak bir yanıttaki tüm özelliklerin patlamasını veya dökümünü göstermek için bu yöntemi kullanıyorum :

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);
    }
  }
}

3
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);
}

3
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"];

3

Aşağıdaki yöntem benim için mükemmel çalışıyor:

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); }
    }
}

Özellik değerini almak için:

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

Özellik değerini ayarlamak için:

t1["prop1"] = value;

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

2

İşte size iç içe yolunu belirtmek için dizeyi gerektirmeyen iç içe bir özellik bulmanın başka bir yolu. Tek mülk yöntemi için Ed S.'ye teşekkür ederiz.

    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);
    }

Kontrol etmek daha iyi olabilir, eğer Type.GetPropertygetiri null yerine çağıran GetValueve sahip NullReferenceExceptionlar bir döngü içinde atılmış.
Groo

2

Hangi nesneyi denetlediğinizden asla bahsetmezsiniz ve belirli bir nesneye başvuran nesneleri reddettiğiniz için, statik bir nesneyi kastettiğimi varsayacağım.

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);
}

Yerel değişkenle denetlenen nesneyi işaretlediğimi unutmayın obj. nullstatik anlamına gelir, aksi takdirde istediğiniz şeye ayarlayın. Ayrıca,GetEntryAssembly() , "çalışan" montajı elde etmek için kullanılabilecek birkaç yöntemden biri , türü yüklemekte zorlanıyorsanız bununla oynamak isteyebilirsiniz.


2

Heleonix.Reflection kütüphanesine bir göz atın . Üyeleri yollarla alabilir / ayarlayabilir / çağırabilir veya yansımadan daha hızlı bir alıcı / ayarlayıcı (bir temsilci olarak derlenen lambda) oluşturabilirsiniz. Örneğin:

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

Veya bir kez bir alıcı oluşturun ve yeniden kullanmak için önbellekleyin (bu daha performanslıdır, ancak ara üye boşsa NullReferenceException özel durumunu atabilir):

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

Veya List<Action<object, object>>farklı alıcılardan bir tane oluşturmak istiyorsanız , derlenmiş temsilciler için temel türleri belirtmeniz yeterlidir (tür dönüşümleri derlenmiş lambdalara eklenecektir):

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

1
Eğer kendi kodunuzda 5-10 satırda makul bir zamanda uygulayabiliyorsanız, asla 3. taraf kütüphanelerini kullanmayın.
Artem G

1

daha kısa yol ....

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 ve AlexD , özellik dizelerinin nasıl çözüleceğine dair mükemmel cevaplar yazdı. Tam olarak bu amaç için özel bir kütüphane yazdığım için, karışıma mayın fırlatmak istiyorum.

Pather.CSharp'ın ana sınıfıResolver . Varsayılan olarak özellikleri, dizi ve sözlük girişlerini çözebilir.

Örneğin, böyle bir nesneniz varsa

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

ve almak istiyorum Property2, bunu şöyle yapabilirsiniz:

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

Bu, çözebileceği yolların en temel örneğidir. Başka neler yapabileceğini veya nasıl genişletebileceğinizi görmek istiyorsanız, Github sayfasına gidin .


0

İşte benim çözümüm. Ayrıca COM nesneleri ile çalışır ve COM nesnelerinden toplama / dizi öğelerine erişilmesini sağlar.

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

İşte diğer cevaplara dayanarak aldıklarım. Hata işlemede bu kadar belirgin olmanın biraz fazla olması.

public static T GetPropertyValue<T>(object sourceInstance, string targetPropertyName, bool throwExceptionIfNotExists = false)
{
    string errorMsg = null;

    try
    {
        if (sourceInstance == null || string.IsNullOrWhiteSpace(targetPropertyName))
        {
            errorMsg = $"Source object is null or property name is null or whitespace. '{targetPropertyName}'";
            Log.Warn(errorMsg);

            if (throwExceptionIfNotExists)
                throw new ArgumentException(errorMsg);
            else
                return default(T);
        }

        Type returnType = typeof(T);
        Type sourceType = sourceInstance.GetType();

        PropertyInfo propertyInfo = sourceType.GetProperty(targetPropertyName, returnType);
        if (propertyInfo == null)
        {
            errorMsg = $"Property name '{targetPropertyName}' of type '{returnType}' not found for source object of type '{sourceType}'";
            Log.Warn(errorMsg);

            if (throwExceptionIfNotExists)
                throw new ArgumentException(errorMsg);
            else
                return default(T);
        }

        return (T)propertyInfo.GetValue(sourceInstance, null);
    }
    catch(Exception ex)
    {
        errorMsg = $"Problem getting property name '{targetPropertyName}' from source instance.";
        Log.Error(errorMsg, ex);

        if (throwExceptionIfNotExists)
            throw;
    }

    return default(T);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.