Поиск свойства в графе объектов через строку - PullRequest
9 голосов
/ 04 мая 2011

Я пытаюсь получить доступ к различным частям структуры вложенного класса, используя произвольную строку.

Учитывая следующие (надуманные) классы:

public class Person
{
   public Address PersonsAddress { get; set; }
}

public class Adddress
{
   public PhoneNumber HousePhone { get; set; }
}

public class PhoneNumber
{
   public string Number { get; set; }
}

Я хотел бы иметь возможность получить объект в "PersonsAddress.HousePhone.Number" из экземпляра объекта Person.

В настоящее время я делаю какой-то броский рекурсивный поиск с использованием отражения, но я надеюсь, что некоторыеУ ниндзя есть несколько лучших идей.

Для справки, вот (дрянной) метод, который я разработал:

private static object ObjectFromString(object basePoint, IEnumerable<string> pathToSearch)
{
   var numberOfPaths = pathToSearch.Count();

   if (numberOfPaths == 0)
     return null;

   var type = basePoint.GetType();
   var properties = type.GetProperties();

   var currentPath = pathToSearch.First();

   var propertyInfo = properties.FirstOrDefault(prop => prop.Name == currentPath);

   if (propertyInfo == null)
     return null;

   var property = propertyInfo.GetValue(basePoint, null);

   if (numberOfPaths == 1)
     return property;

   return ObjectFromString(property, pathToSearch.Skip(1));
}

Ответы [ 4 ]

14 голосов
/ 25 мая 2011

Вы можете просто использовать стандартный метод .NET DataBinder.Eval , например:

object result = DataBinder.Eval(myPerson, "PersonsAddress.HousePhone.Number");
4 голосов
/ 24 мая 2011

Мне приходилось что-то похожее в прошлом. Я пошел с лямбда-подходом, потому что после компиляции я могу их кешировать. Я удалил кеширование в этом коде.

Я включил несколько модульных тестов, чтобы показать использование метода. Я надеюсь, что это полезно.

private static object GetValueForPropertyOrField( object objectThatContainsPropertyName, IEnumerable<string> properties )
  {
     foreach ( var property in properties )
     {
        Type typeOfCurrentObject = objectThatContainsPropertyName.GetType();

        var parameterExpression = Expression.Parameter( typeOfCurrentObject, "obj" );
        Expression memberExpression = Expression.PropertyOrField( parameterExpression, property );

        var expression = Expression.Lambda( Expression.GetDelegateType( typeOfCurrentObject, memberExpression.Type ), memberExpression, parameterExpression ).Compile();

        objectThatContainsPropertyName = expression.DynamicInvoke( objectThatContainsPropertyName );
     }

     return objectThatContainsPropertyName;
  }

  [TestMethod]
  public void TestOneProperty()
  {
     var dateTime = new DateTime();

     var result = GetValueForPropertyOrField( dateTime, new[] { "Day" } );

     Assert.AreEqual( dateTime.Day, result );
  }

  [TestMethod]
  public void TestNestedProperties()
  {
     var dateTime = new DateTime();

     var result = GetValueForPropertyOrField( dateTime,  new[] { "Date", "Day" } );

     Assert.AreEqual( dateTime.Date.Day, result );
  }

  [TestMethod]
  public void TestDifferentNestedProperties()
  {
     var dateTime = new DateTime();

     var result = GetValueForPropertyOrField( dateTime, new[] { "Date", "DayOfWeek" } );

     Assert.AreEqual( dateTime.Date.DayOfWeek, result );
  }
3 голосов
/ 04 мая 2011

Вот нерекурсивная версия с (почти) такой же семантикой:

private static object ObjectFromString(object basePoint, IEnumerable<string> pathToSearch)
{
    var value = basePoint;
    foreach (var propertyName in pathToSearch)
    {
        var property = value.GetType().GetProperty(propertyName);
        if (property == null) return null;
        value = property.GetValue(value, null);
    }
    return value;
}
1 голос
/ 28 мая 2011

Поскольку вы уже заинтересованы в разрешении путей свойств строк, вы можете воспользоваться библиотекой запросов Dynamic LINQ , опубликованной в качестве примера Скоттом Гатри @ Microsoft. Он анализирует ваши строковые выражения и создает деревья экспрессов, которые можно скомпилировать и кэшировать в соответствии с предложением @Brian Dishaw.

Это предоставит вам множество дополнительных опций, предоставляя простой и надежный синтаксис выражений, который вы можете использовать в своем подходе к настройке. Он поддерживает общие методы LINQ для перечислимых элементов, а также простую операторную логику, математические вычисления, оценку пути к свойствам и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...