Как написать собственный класс DynamicObject, поддерживающий инициализаторы объектов
-
29-10-2019 - |
Вопрос
В документации для DynamicObject естьпример кода сгенерированного тега, который позволяет вам работать со словарем, как если бы это класс со свойствами.
Вот класс (слегка измененный для краткости):
родовое словоЯ бы хотел изменить класс, чтобы я мог делать следующее:
родовое слово< sizesВопросы
- Возможно ли это?
- Если да, то как?
Решение 3
Оказывается, я смог решить мою проблему, используя встроенный метод GensacodicCode Linq.
Пример:
public Test()
{
var data = Enumerable.Range(1, 5).Select(i => new
{
Id = i,
Foo = "Foo",
Bar = 2
});
var result = data
.Select(d => d.GetType().GetProperties()
.Select(p => new { Name = p.Name, Value = p.GetValue(pd, null) })
.ToDictionary(
pair => pair.Name,
pair => pair.Value == null ? string.Empty : pair.Value.ToString()));
}
. Другие советы
DynamicObject
предоставляет TryCreateInstance()
, которыйпредназначен для подобных ситуаций, но его нельзя использовать в C #.
Я вижу несколько способов обойти это:
-
Создайте динамический фабричный класс.Когда вы вызываете его метод
Create()
с именованными аргументами, он передает его в словарь:class DynamicDictionaryFactory : DynamicObject { public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, out object result) { if (binder.Name == "Create") { // use binder.CallInfo.ArgumentNames and args // to create the dynamic dictionary result = …; return true; } return base.TryInvokeMember(binder, args, out result); } } … dynamic factory = new DynamicDictionaryFactory(); dynamic dict = factory.Create(Id: 42);
-
Использовать нединамический инициализатор коллекции.Это означает, что имена свойств должны быть в коде в виде строк:
// has to implement IEnumerable, so that collection initializer works class DynamicDictionary : DynamicObject, IEnumerable<KeyValuePair<string, object>> { public void Add(string name, object value) { m_dictionary.Add(name, value); } // IEnumerable implmentation and actual DynamicDictionary code here } … dynamic dict = new DynamicDictionary { { "Id", 42 } };
-
Вероятно, наиболее близким к тому, что вы просили, было бы использование инициализатора вложенного объекта.То есть класс будет иметь свойство
dynamic
(скажем,Values
), свойства которого можно установить с помощью инициализатора объекта:class DynamicDictionary : DynamicObject { private readonly IDictionary<string, object> m_expandoObject = new ExpandoObject(); public dynamic Values { get { return m_expandoObject; } } // DynamicDictionary implementation that uses m_expandoObject here } … dynamic dict = new DynamicDictionary { Values = { Id = 42 } };
Используя ImpromptuInterface с открытым исходным кодом (через nuget), он имеет синтаксис Builder .это позволяет вам делать что-то близкое к синтаксису инициализации.В частности, после включения кода ImpromptuInterface.Dynamic
вы можете сделать
Есть и другие варианты, перечисленные на этой странице синтаксиса, если вы удалите код <DynamicDictionary>
, он будет использовать код ImpromptuDictionary
, что по сути то же самое.И вы можете посмотреть исходный код также для синтаксиса сборки.
Когда я вижу подобный образец встроенного языка, я обычно использую методы расширения, поэтому для достижения своей цели я могу написать следующий код:
родовое словоЗатем я определил бы метод расширения с кодом, чтобы преобразовать любой генерируемый кодовый код (включая анонимно типизированный) в генерирующий кодовый код:
родовое слово Вам нужно будет реализовать конструктор в object
, чтобы получить код DynamicDictionary
, но это совсем несложно.