Как я уже упоминал в своем комментарии, вы задаете два отдельных вопроса, которые только слабо связаны, но я приложу все усилия, чтобы объяснить различие и ответить на них оба:
Вопрос № 1: Как бы вы использовали HasValue
для обнуляемых свойств динамических c объектов?
Короткий ответ:; Вы не можете без распаковки. Например, вы можете распаковать свойство Guid
в своем коде следующим образом:
((Guid?)person.Guid).HasValue
Причину этого можно объяснить, изучив некоторые характеристики dynamic
и Nullable
:
dynamic
является концепцией компиляции и не существует в времени выполнения . Так что под капотом это просто object
. - . При использовании переменной
dynamic
любое дочернее свойство и \ или метод также присуще dynamic
. - A
Nullable<>
не упаковывается как есть. Вместо этого вы получите либо нулевую ссылку на объект, если это null
, либо , вы получите базовый тип (например, System.Guid
).
Что это означает для ваш пример: person
упаковывается вместе со свойством Guid
. Поскольку свойство Guid
имеет значение NULL, оно помечается как или System.Guid
(если оно не null
) или null
, устраняя возможность использования этого свойства ( что может привести к нескольким различным исключениям времени выполнения).
Другим, возможно, более простым решением, будет просто сравнить его с null
. Например:
person.Guid != null
Вопрос № 2: Как бы вы преобразовали весь граф объектов в ExpandoObject
?
В отличие от предыдущего вопроса, Решение этого вопроса, возможно, проще найти asp (хотя для его реализации может потребоваться больше кода).
Чтобы рекурсивно преобразовать весь граф объектов в ExpandoObject
, вы можете просто сделать свою функцию рекурсивной. Например:
public ExpandoObject ToExpando(object @object)
{
var properties = @object.GetType().GetProperties();
IDictionary<string, object> expando = new ExpandoObject();
foreach (var property in properties)
{
var value = GetValueOrExpandoObject(@object, property);
expando.Add(property.Name, value);
}
return (ExpandoObject)expando;
}
public object GetValueOrExpandoObject(object @object, PropertyInfo property)
{
var value = property.GetValue(@object);
if (value == null) return null;
var valueType = value.GetType();
if (valueType.IsValueType || value is string) return value;
if (value is IEnumerable enumerable) return ToExpandoCollection(enumerable);
return ToExpando(value);
}
public IEnumerable<ExpandoObject> ToExpandoCollection(IEnumerable enumerable)
{
var enumerator = enumerable.GetEnumerator();
while (enumerator.MoveNext())
{
yield return ToExpando(enumerator.Current);
}
}
В этом примере я (наивно) проверяю для каждого свойства, должно ли значение быть преобразовано в ExpandoObject
(например, если это тип значения), и если это Позвоните по номеру ToExpando
, указав значение свойства.
Обратите внимание, что это далеко не идеальное решение, но оно показывает, как быстро это может выйти из-под контроля. Чем больше типов вам нужно обрабатывать по-разному, тем сложнее это будет, поэтому, если вы в итоге реализуете что-то подобное, вы должны рассмотреть шаблон посетителя .
Наконец, я Хотелось бы отметить, что в целом я настоятельно рекомендую вам избегать , используя, если возможно, полностью dynamic
, поскольку это очень дорого как с технической, так и с познавательной точки зрения (т.е. понимая, что такое код делает и что может go не так).