.Net Получить значения свойств объекта по ключу на любую глубину - PullRequest
2 голосов
/ 07 сентября 2010

Я бы хотел иметь возможность доступа к значению свойства объекта на любой глубине, имеющей только строковый ключ свойства.Также, если возможно, используйте индексацию коллекций в свойствах List.Итак, если у меня есть строка «Person.Surname», я мог бы получить значение «Smith» и создать экземпляр объекта CaseConductor.Итак, учитывая некоторый код установки, подобный этому ...

//- Load a caseConductor
var caseConductor = new CaseConductor();
caseConductor.CaseID = "A00001"; 
// person 
caseConductor.Person = new Person();
caseConductor.Person.Surname = "Smith" ;
caseConductor.Person.DOB = DateTime.Now ; 
// case note list
caseConductor.CaseNoteList = new List<Note>();
caseConductor.CaseNoteList.Add(new Note { NoteText = "A-1" , NoteDt  = DateTime.Now });
caseConductor.CaseNoteList.Add(new Note { NoteText = "B-2", NoteDt = DateTime.Now });
// I could do this ...
object val = caseConductor.SomeCleverFunction("Person.Surname");
// or this ...
object val = caseConductor.SomeCleverFunction("CaseNoteList[0].NoteText");

Кто-нибудь делал это раньше?Вот некоторые установочные классы ...

class Note
    {
        public Guid NoteID { get; set; }
        public string NoteText { get; set; }
        public DateTime? NoteDt { get; set; }
    }
    public class Person
    {
        public Guid PersonID { get; set; }
        public string Surname { get; set; }
        public string Forename { get; set; }
        public DateTime? DOB { get; set; }
    }
    class CaseConductor
    {
        public String CaseID{get;set;}
        public Person Person { get; set; }
        public List<Note> CaseNoteList { get; set; }
    }

Наш вариант использования состоит в том, чтобы перебрать серию соответствующим образом названных элементов управления содержимым в шаблоне Word Word, используя open xml sdk 2, и вставить значения во вновь созданное словодокументы, как то так ...

List<SdtElement> ccList = wordprocessingDocument.MainDocumentPart.Document.Descendants<SdtElement>().ToList();
foreach (var cc in ccList)
{
  string alias = cc.SdtProperties.GetFirstChild<SdtAlias>().Val.Value;
  switch (cc.GetType().Name)
  {
    case "SdtRun":
      SdtRun thisRun = (SdtRun)cc;
      //thisRun.Descendants<Text>().First().Text  = theValueToBePoked ; 
      break;
   }
}

Ответы [ 3 ]

4 голосов
/ 07 сентября 2010

Используйте старые добрые отражения.Я проверил, и это на самом деле работает:

    public static object GetValue(object o, string propertyName)
    {
        Type type = o.GetType();
        PropertyInfo propertyInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance ).Where(x => x.Name == propertyName).FirstOrDefault();
        if(propertyInfo!=null)
        {
            return propertyInfo.GetValue(o, BindingFlags.Instance, null, null, null);
        }
        else
        {
            return null; // or throw exception 
        }
    }
2 голосов
/ 07 сентября 2010

Я предполагаю, что caseConductor.SomeCleverFunction не является статическим методом и имеет доступ к объекту Person и самому объекту Person, если оно является публичным свойством.

Я также предполагаю, что вы хотите передать строку типа "prop.address.street", где каждое вспомогательное свойство является классом, который содержит свойство puplic с таким именем

  1. Разделить строку ввода на период, найти самую левую строку
  2. Используйте отражение, чтобы получить список свойств (typeof (caseconductor) .GetProperties ())
  3. Найдите соответствующее свойство, вызовите для него метод GetValue, передав последний известный твердый объект (начиная с 'this') и сохранив ссылку на него.
  4. если в строке осталось больше под-свойств, повторите шаг 1, удалив самую левую часть строки.
  5. в противном случае вызовите GetValue () для свойства, используя последний объект возврата GetValue (), полученный на шаге 3, и верните его.

Что-то вроде:

"prop.address.street" -> найти свойство "prop" из 'this' и GetValue,

еще больше ".", Поэтому повторите, сохраняя возвращаемое значение

"address.street" -> найти свойство "address" из последнего возвращенного GetValue и получить его значение.

еще больше ".", Поэтому повторите, сохраняя возвращаемое значение

"улица" -> найти свойство "улица" из последнего возвращенного GetValue и вернуть его значение.

Конец строки, возвращает последнее значение

Редактировать -

Это довольно грубо, но добавьте его в LinqPAD и посмотрите.

http://www.linqpad.net/

Edit # 2 - вы должны иметь возможность индексировать массивы, используя синтаксис ^ ниже.

Опять же, это очень грубо, достаточно, чтобы получить рабочий пример.

Edit # 3 - Немного очистил пример и изменил его с моих классов-примеров на ваши.

void Main()
{
 //- Load a caseConductor 
 var caseConductor = new CaseConductor(); 
 caseConductor.CaseID = "A00001";  
 // person  
 caseConductor.Person = new Person(); 
 caseConductor.Person.Surname = "Smith" ; 
 caseConductor.Person.DOB = DateTime.Now ;  
 // case note list 
 caseConductor.CaseNoteList = new List<Note>(); 
 caseConductor.CaseNoteList.Add(new Note { NoteText = "A-1" , NoteDt  = DateTime.Now }); 
 caseConductor.CaseNoteList.Add(new Note { NoteText = "B-2", NoteDt = DateTime.Now }); 
 // I could do this ... 
 string val1 = caseConductor.GetPropertyValue<string>("Person.Surname"); 
 // or this ... 
 Note val2 =  caseConductor.GetPropertyValue<Note>("CaseNoteList^1"); 
 val1.Dump("val1"); //this is a string
 val2.Dump("val2"); //this is a Note
}

public static class extensions
{
 public static T GetPropertyValue<T>(this object o,string Properties) where T:class
 {

  var properties = Properties.Split('.');
  var indexsplit = properties[0].Split('^');

  var current = indexsplit[0];


  var prop = (from p  in o.GetType().GetProperties() where p.Name == current select p).Take(1).Single();
  var val = prop.GetValue(o,null);

  if(indexsplit.Length>1)
  {
   var index = int.Parse(indexsplit[1]);
   IList ival = (IList)val;
   val = ival[index];
  }

  if(properties[0] == Properties)
   return (T)val;
  else
   return val.GetPropertyValue<T>(Properties.Replace(properties[0]+".",""));
 }
}


class Note 
 { 
  public Guid NoteID { get; set; } 
  public string NoteText { get; set; } 
  public DateTime? NoteDt { get; set; } 
 } 
 public class Person 
 { 
  public Guid PersonID { get; set; } 
  public string Surname { get; set; } 
  public string Forename { get; set; } 
  public DateTime? DOB { get; set; } 
 } 
 class CaseConductor 
 { 
  public String CaseID{get;set;} 
  public Person Person { get; set; } 
  public List<Note> CaseNoteList { get; set; } 
 } 
0 голосов
/ 08 сентября 2010

ОК, я придумала что-то, что продолжает Aliosted и asowyer начала предложения, вот оно Вы можете видеть, что у меня все еще есть проблемы с доступом к индексу составных объектов. Спасибо за вашу помощь.

    #region object data ...
    var model = new HcmlDocumentProductionModel();
    model.CaseID = "A001";
    model.CaseConductor = new CaseConductor();
    model.CaseConductor.AField = "AField";
    model.CaseConductor.Person = new Person();
    model.CaseConductor.Person.Surname = "{Smith}";
    model.CaseConductor.Person.DOB = DateTime.Now;
    model.CaseConductor.CaseNoteList = new List<Note>();
    model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "A-1", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.CaseNote });
    model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "B-2", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.ReferralNote });
    model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "C-3", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.StatusNote });
    model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "d-3", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.CaseNote });
    model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "e-3", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.StatusNote });
    model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "f-3", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.CaseNote });
    #endregion

    string head = "";
    string tail = "";

    // tail
    tail = "".Tail();
    tail = "Surname".Tail();
    tail = "Person.Surname".Tail();
    tail = "CaseConductor.Person.Surname".Tail();

    // head 
    head = "".Head();
    head = "Surname".Head();
    head = "Person.Surname".Head();
    head = "CaseConductor.Person.Surname".Head();

    // ObjectDictionary
    //var person = new Person { Surname = "Smith" };
    //var d = person.ObjectDictionary();
    //object ovalue = d["Surname"]; 

    // get value special
    object o2 = model.CaseConductor.Person.ValueByKey("Surname");
    object o3 = model.CaseConductor.Person.ValueByKey("DOB");
    object o4 = model.CaseConductor.ValueByKey("Person.Surname");
    object o5 = model.ValueByKey("CaseConductor.Person.Surname");

    // get the list of ...
    object o6 = model.ValueByKey("CaseConductor.CaseNoteList");

    // get item - index thing does not work - get anull here
    string noteText = model.CaseConductor.CaseNoteList[1].NoteText;
    object o7 = model.ValueByKey("CaseConductor.CaseNoteList[1].NoteText");

namespace Zed
{
    public static class Zed
    {
        public static object ValueByKey(this object o, string key)
        {
            if (!String.IsNullOrEmpty(key))
            {
                if (!key.Contains("."))
                {
                    return (o.ObjectDictionary())[key];
                }
                else
                {
                    // key contains a dot ; therefore get object by the name of the head 
                    // and pass on that object and get propety by the tail
                    var d = o.ObjectDictionary();
                    var head = key.Head();
                    if (head.Contains("["))
                    {
                        string headMinusIndexer = head.Substring(0, head.IndexOf("["));
                        string indexString = head.Between("[", "]");
                        int index = Convert.ToInt32(indexString);
                        object oArray = d[headMinusIndexer];
                        //List<object> oList= d[headMinusIndexer]; 
                        // now get the object with the index, ... and continue
                        //object el = ((object[])oArray)[index];
                        return null;
                    }
                    else
                    {
                        var onext = d[head];
                        return onext.ValueByKey(key.Tail());
                    }

                }
            }
            return null;
        }

        public static Dictionary<string,object> ObjectDictionary(this object o)
        {
            return o.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(o, null));
        }
        public static string Head(this  string key)
        {
            var head = String.Empty;
            var splittBy = '.';
            if (!String.IsNullOrEmpty(key))
            {
                var keyArray = key.Split(splittBy);
                head = keyArray[0];
            }
            //-Return
            return head;
        }
        public static string Tail(this string key)
        {
            var tail = "";
            var splittBy = '.';
            if (!String.IsNullOrEmpty(key))
            {
                var keyArray = key.Split(splittBy);
                for (int i = 1; i < keyArray.Length; i++)
                {
                    tail += (i > 1) ? "." + keyArray[i] : keyArray[i];
                }
            }
            //-Return
            return tail;
        }
        public static string Between(this string head, string start, string end)
        {
            string between = String.Empty ;
            between = head.Substring(head.IndexOf(start) + 1, head.IndexOf(end) - head.IndexOf(start) - 1);
            return between;
        }

        public static object ZGetValue( this object o, string propertyName)
        {
            Type type = o.GetType();
            PropertyInfo propertyInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name == propertyName).FirstOrDefault();
            if (propertyInfo != null)
            {
                return propertyInfo.GetValue(o, BindingFlags.Instance, null, null, null);
            }
            else
            {
                return null;
            }
        }
    }

}
...