รับค่าคุณสมบัติจากสตริงโดยใช้การสะท้อนใน C #


928

ฉันพยายามใช้การแปลงข้อมูลโดยใช้ตัวอย่างReflection 1ในรหัสของฉัน

GetSourceValueฟังก์ชั่นที่มีสวิทช์เปรียบเทียบหลากหลายชนิด แต่ฉันต้องการที่จะลบประเภทนี้และคุณสมบัติและมีการGetSourceValueรับค่าของทรัพย์สินที่ใช้เพียงสายเดียวที่เป็นพารามิเตอร์ ฉันต้องการส่งคลาสและคุณสมบัติในสตริงและแก้ไขค่าของคุณสมบัติ

เป็นไปได้ไหม

1 Web Archive โพสต์บล็อกดั้งเดิม

คำตอบ:


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

แน่นอนคุณจะต้องเพิ่มการตรวจสอบและ whatnot แต่นั่นคือส่วนสำคัญของมัน


8
ดีและเรียบง่าย! ฉันต้องการทำให้เป็นเรื่องทั่วไป:public static T GetPropertyValue<T>(object obj, string propName) { return (T)obj.GetType().GetProperty(propName).GetValue(obj, null); }
— Ohad Schneider

2
การเพิ่มประสิทธิภาพสามารถลบความเสี่ยงของข้อยกเว้นโมฆะเช่นนี้: " src.GetType().GetProperty(propName)?.GetValue(src, null);";)
— shA.t

8
@ shA.t: ฉันคิดว่านั่นเป็นความคิดที่ไม่ดี คุณจะแยกความแตกต่างระหว่างค่า Null ของคุณสมบัติที่มีอยู่หรือไม่มีคุณสมบัติได้อย่างไร ฉันค่อนข้างจะรู้ทันทีว่าฉันส่งชื่ออสังหาริมทรัพย์ที่ไม่ดี นี่ไม่ใช่รหัสการผลิต แต่การปรับปรุงที่ดีกว่าคือการโยนข้อยกเว้นที่เฉพาะเจาะจงมากขึ้น (เช่นตรวจสอบการเปิดGetPropertyและการโยนPropertyNotFoundExceptionหรือสิ่งใด ๆ ที่เป็นโมฆะ)
— 35499 Ed Ed

210

เกี่ยวกับบางสิ่งเช่นนี้:

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

คุณสามารถใช้วิธีการเหล่านี้เป็นวิธีการคงที่หรือส่วนขยาย


3
@ FredJand ดีใจที่คุณสะดุดมัน! มันน่าประหลาดใจเสมอเมื่อโพสต์เก่าเหล่านี้เปิดขึ้น มันค่อนข้างคลุมเครือเล็กน้อยดังนั้นฉันจึงเพิ่มข้อความเล็กน้อยเพื่ออธิบาย ฉันได้เปลี่ยนไปใช้วิธีการเหล่านี้เป็นส่วนขยายและเพิ่มรูปแบบ generics ดังนั้นฉันจึงเพิ่มที่นี่
— jheddings

ทำไมตัวป้องกัน null ใน foreach และไม่อยู่ด้านบน
— Santhos

4
@Santos เนื่องจาก 'obj' ถูกนิยามใหม่ในเนื้อความของ foreach loop จะถูกตรวจสอบในระหว่างการทำซ้ำแต่ละครั้ง
— jheddings

มีประโยชน์ แต่ในกรณีขอบที่หนึ่งในคุณสมบัติซ้อนกันอาจถูกซ่อนไว้ (โดยใช้ตัวดัดแปลง 'ใหม่') มันจะส่งข้อยกเว้นสำหรับการค้นหาคุณสมบัติที่ซ้ำกัน มันจะเป็น neater เพื่อติดตามชนิดของคุณสมบัติล่าสุดและใช้PropertyInfo.PropertyTypeแทนobj.GetType()คุณสมบัติซ้อนกันเช่นเดียวกับการเข้าถึงคุณสมบัติบนคุณสมบัติที่ซ้อนกัน
— Nullius

คุณสามารถใช้nameofนิพจน์ตั้งแต่ C # 6 ดังนี้: nameof(TimeOfDay.Minutes)บนพารามิเตอร์ name เมื่อเรียกใช้ฟังก์ชันเพื่อกำจัดสายเวทย์มนตร์และเพิ่มความปลอดภัยเวลาคอมไพล์ให้กับการเรียกเหล่านี้
— เรียบ

74

เพิ่มไปที่ใด ๆ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: เป็นไปได้ไหมที่จะใช้การไตร่ตรองกับสิ่งนี้ดังนั้นคุณไม่จำเป็นต้องรู้ว่าสมาชิกของคลาสมีอะไรบ้าง?
— คนของเราใน Bananas

เป็นไปได้ไหมถ้า "บาร์" เป็นวัตถุ?
— big_water

@big_water SetValueและวิธีการทำงานร่วมกับGetValue Objectหากคุณต้องการทำงานกับประเภทเฉพาะคุณควรส่งผลลัพธ์GetValueและใช้เพื่อกำหนดค่าด้วยSetValue
— Eduardo Cuomo

ขออภัย @ OurManinBananas ฉันไม่เข้าใจคำถามของคุณ เธออยากทำอะไรล่ะ?
— Eduardo Cuomo

วิธีการประเภทนี้ชื่ออะไร?
— Sahan Chinthaka

45

สิ่งที่เกี่ยวกับการใช้CallByNameของMicrosoft.VisualBasicnamespace ( Microsoft.VisualBasic.dll)? มันใช้การสะท้อนเพื่อรับคุณสมบัติเขตข้อมูลและวิธีการของวัตถุปกติวัตถุ COM และแม้แต่วัตถุแบบไดนามิก

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

แล้ว

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

5
คำแนะนำที่น่าสนใจการตรวจสอบเพิ่มเติมพิสูจน์ว่าสามารถจัดการได้ทั้งฟิลด์และคุณสมบัติวัตถุ COM และสามารถจัดการการเชื่อมโยงแบบไดนามิกได้อย่างถูกต้อง !
— IllidanS4 ต้องการโมนิก้ากลับ

ฉันได้รับข้อผิดพลาด: สมาชิกสาธารณะ 'MyPropertyName' บนประเภท 'MyType' ไม่พบ
— vldmrrdjcc

30

คำตอบที่ดีโดย jheddings ฉันต้องการที่จะปรับปรุงให้สามารถอ้างอิงอาเรย์รวมหรือคอลเลกชันของวัตถุเพื่อให้ 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 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]
— Jesse Adam

เป็น Array -> เป็นวัตถุ [] ส่งผลให้เกิดข้อยกเว้น Nullreference อะไรที่เหมาะกับฉัน (prop ไม่ใช่วิธีที่มีประสิทธิภาพมากที่สุด) คือการส่ง unknownCollection ไปที่ IEnumerable และมากกว่าใช้ ToArray () กับผลลัพธ์ ซอ
— Jeroen Jonkman

14

ถ้าฉันใช้รหัสจากEd S.ฉันจะได้รับ

'ReflectionExtensions.GetProperty (ประเภท, สตริง)' ไม่สามารถเข้าถึงได้เนื่องจากระดับการป้องกัน

ดูเหมือนว่าGetProperty()ไม่สามารถใช้ได้ใน Xamarin.Forms TargetFrameworkProfileอยู่Profile7ใน Portable Class Library ของฉัน (.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);
}

แหล่ง


4
การปรับปรุงเพียงเล็กน้อยที่เป็นไปได้ แทนที่ถ้า IF และส่งคืนโดย: return property? .GetValue (แหล่งที่มา);
— Tomino

11

เกี่ยวกับการสนทนาคุณสมบัติที่ซ้อนกันคุณสามารถหลีกเลี่ยงสิ่งสะท้อนทั้งหมดถ้าคุณใช้DataBinder.Eval Method (Object, String)ดังต่อไปนี้:

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

แน่นอนคุณจะต้องเพิ่มการอ้างอิงไปยังSystem.Webแอสเซมบลี แต่นี่อาจไม่ใช่เรื่องใหญ่


8

วิธีการโทรมีการเปลี่ยนแปลงใน. NET Standard (จนถึง 1.6) นอกจากนี้เราสามารถใช้โอเปอเรเตอร์ที่มีเงื่อนไขของ C # 6

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

1
ขึ้นสำหรับการใช้? operator
— blfuentes

4

การใช้ PropertyInfo ของSystem.Reflection namespace การรวบรวมภาพสะท้อนนั้นทำได้ดีไม่ว่าเราจะพยายามเข้าถึงทรัพย์สินใด ข้อผิดพลาดจะเกิดขึ้นในช่วงเวลาทำงาน

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

ใช้งานได้ดีเพื่อรับคุณสมบัติตำแหน่งของวัตถุ

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

เราจะได้ตำแหน่ง: {X = 71, Y = 27} นอกจากนี้เรายังสามารถส่งคืนตำแหน่ง X หรือตำแหน่ง 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;
    }

นี่คือวิธีรับคุณสมบัติทั้งหมดพร้อมค่าในรายการ


ทำไมจะทำเช่นนี้: 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

3

รหัสต่อไปนี้เป็นวิธีการเรียกซ้ำสำหรับการแสดงลำดับชั้นทั้งหมดของชื่อคุณสมบัติและค่าทั้งหมดที่มีอยู่ในอินสแตนซ์ของวัตถุ วิธีนี้ใช้GetPropertyValue()คำตอบของ AlexD ในเวอร์ชันที่เรียบง่ายด้านบนในชุดข้อความนี้ ขอบคุณกระทู้สนทนานี้ฉันสามารถหาวิธีการทำสิ่งนี้!

ตัวอย่างเช่นฉันใช้วิธีนี้เพื่อแสดงการระเบิดหรือการถ่ายโอนข้อมูลของคุณสมบัติทั้งหมดในการ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);
    }
  }
}

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

วิธีการด้านล่างใช้งานได้สมบูรณ์แบบสำหรับฉัน:

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;


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

objหมายเหตุที่ผมทำเครื่องหมายวัตถุที่จะถูกตรวจสอบกับตัวแปรท้องถิ่น nullหมายถึงคงที่มิฉะนั้นกำหนดเป็นสิ่งที่คุณต้องการ นอกจากนี้โปรดทราบว่าวิธีGetEntryAssembly()นี้เป็นวิธีการหนึ่งในไม่กี่วิธีในการรับชุดประกอบ "ที่กำลังทำงานอยู่" คุณอาจต้องการเล่นกับมันหากคุณประสบปัญหาในการโหลดประเภท


2

ลองดูที่Heleonix.Reflection library คุณสามารถรับ / set / เรียกใช้สมาชิกตามเส้นทางหรือสร้าง getter / setter (แลมบ์ดาที่รวบรวมเป็นตัวแทน) ซึ่งเร็วกว่าการสะท้อนกลับ ตัวอย่างเช่น:

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

หรือสร้างผู้ทะเยอทะยานหนึ่งครั้งและแคชเพื่อนำมาใช้ใหม่ (นี่คือประสิทธิภาพมากกว่า แต่อาจโยน NullReferenceException หากสมาชิกระดับกลางเป็นโมฆะ):

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

หรือถ้าคุณต้องการสร้างผู้ได้รับที่List<Action<object, object>>ต่างกันเพียงแค่ระบุประเภทฐานสำหรับผู้ได้รับการรวบรวม (การแปลงประเภทจะถูกเพิ่มลงใน lambdas ที่คอมไพล์แล้ว):

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

1
อย่าใช้ libs ของบุคคลที่สามหากคุณสามารถนำไปใช้ในรหัสของคุณเองในเวลาที่เหมาะสมใน 5-10 บรรทัด
— Artem G

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.CSharpResolverระดับหลักก็คือ โดยค่าเริ่มต้นสามารถแก้ไขรายการคุณสมบัติอาร์เรย์และพจนานุกรม

ตัวอย่างเช่นถ้าคุณมีวัตถุแบบนี้

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

นี่คือสิ่งที่ฉันได้จากคำตอบอื่น ๆ overkill เล็กน้อยในการรับเฉพาะกับการจัดการข้อผิดพลาด

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);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.