Обработать объект, как словарь свойств в С#

Я хочу иметь доступ к значениям свойств в объекте, таком как словарь, используя имя свойства в качестве ключа. Мне все равно, вернутся ли значения как объекты, поэтому Dictionary<string, object> в порядке. Это предполагаемое использование:

object person = new { Name: "Bob", Age: 45 };
IDictionary<string, object> lookup = new PropertyDictionary(person);
string name = (string)person["Name"];
person["Age"] = (int)person["Age"] + 1; // potentially editable

Я собирался реализовать свой собственный класс для этого, но затем я начал замечать такие классы, как DynamicObject, реализовать интерфейс IDictionary, который заставлял думать, что это уже делается для меня где-то.

То, что я хочу, похоже на функциональность, используемую ASP.NET MVC, которая позволяет использовать анонимные типы для установки атрибутов HTML-тегов. У меня есть много классов, которые используют словари в качестве источников данных, но большую часть времени я также должен иметь возможность передавать объекты.

Поскольку это для общедоступной библиотеки, я думал, что создам класс многократного использования, который просто украсил объект интерфейсом IDictionary. Это избавит меня от создания перегрузки перегрузок.

Ответ 1

Я не верю, что есть встроенный .Net-тип, подобный этому уже в среде .Net. Похоже, вы действительно хотите создать объект, который ведет себя как объект Javascript. Если это так, то выбор из DynamicObject может быть правильным выбором. Он позволяет создать объект, который при обертке с помощью dynamic позволяет напрямую связывать obj.Name или с помощью индексатора obj["Name"].

public class PropertyBag : DynamicObject {
  private object _source;
  public PropertyBag(object source) {
    _source = source;
  }
  public object GetProperty(string name) {  
    var type = _source.GetType();
    var property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    return property.GetValue(_source, null);
  }
  public override bool TryGetMember(GetMemberBinder binder, out object result) {
    result = GetProperty(binder.Name);
    return true;
  }
  public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) {
    result = GetProperty((string)indexes[0]);
    return true;
  }
}

Вы можете использовать это, чтобы обернуть любой тип и использовать синтаксис индексатора и имени, чтобы получить свойства

var student = new Student() { FirstName = "John", LastName = "Doe" };
dynamic bag = new PropertyBag(student);
Console.WriteLine(bag["FirstName"]);  // Prints: John
Console.WriteLine(bag.FirstName);     // Prints: John

Ответ 2

У меня есть этот метод расширения, возможно, самый простой, который он может получить:

public static Dictionary<string, object> ToPropertyDictionary(this object obj)
{
    var dictionary = new Dictionary<string, object>();
    foreach (var propertyInfo in obj.GetType().GetProperties())
        if (propertyInfo.CanRead && propertyInfo.GetIndexParameters().Length == 0)
            dictionary[propertyInfo.Name] = propertyInfo.GetValue(obj, null);
    return dictionary;
}

Теперь вы можете сделать:

object person = new { Name = "Bob", Age = 45 };
var lookup = person.ToPropertyDictionary();
string name = (string)lookup["Name"];
lookup["Age"] = (int)lookup["Age"] + 1; // indeed editable

Примечание:

  • что этот словарь чувствителен к регистру (вы можете тривиально расширять его, передавая правый StringComparer).

  • что он игнорирует индексаторы (которые также являются свойствами), но это зависит от вас, чтобы работать над ним.

  • что метод не является общим, поскольку он не помогает боксу, потому что внутри он вызывает obj.GetType, поэтому он все равно помещается в эту точку.

  • что вы получаете только "читаемые" свойства (иначе вы не получите значения, содержащиеся в нем). Поскольку вы хотите, чтобы он был "доступен для записи", вы также должны использовать флаг CanWrite.

Ответ 3

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

следуйте этим ссылкам MSDN:

Использование динамического ключевого слова в С#
динамический (ссылка С#)
Обзор DLR
использование страницы динамической выборки