it-swarm.com.ru

Как получить доступ к свойству анонимного типа в C #?

У меня есть это:

List<object> nodes = new List<object>(); 

nodes.Add(
new {
    Checked     = false,
    depth       = 1,
    id          = "div_" + d.Id
});

... и мне интересно, смогу ли я тогда получить свойство "Проверено" анонимного объекта. Я не уверен, возможно ли это вообще. Попробовал сделать это:

if (nodes.Any(n => n["Checked"] == false)) ... но это не работает.

Спасибо

94
wgpubs

Если вы хотите получить строго типизированный список анонимных типов, вам нужно также сделать список анонимным типом. Самый простой способ сделать это - проецировать последовательность, такую ​​как массив, в список, например,.

var nodes = (new[] { new { Checked = false, /* etc */ } }).ToList();

Тогда вы сможете получить к нему доступ, как:

nodes.Any(n => n.Checked);

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

nodes.Add(new { Checked = false, /* etc */ });
54
Greg Beech

Если вы храните объект как тип object, вам нужно использовать отражение. Это верно для любого типа объекта, анонимного или иного. На объекте o вы можете получить его тип:

Type t = o.GetType();

Затем из этого вы ищите свойство:

PropertyInfo p = t.GetProperty("Foo");

Тогда из этого вы можете получить значение:

object v = p.GetValue(o, null);

Этот ответ давно пора обновить для C # 4:

dynamic d = o;
object v = d.Foo;

А теперь еще одна альтернатива в C # 6:

object v = o?.GetType().GetProperty("Foo")?.GetValue(o, null);

Обратите внимание, что при использовании ?. мы получаем, что результирующая v будет null в трех разных ситуациях! 

  1. o - это null, поэтому объекта вообще нет
  2. o не -null, но не имеет свойства Foo
  3. o имеет свойство Foo, но его реальное значение оказывается null.

Так что это не эквивалентно более ранним примерам, но может иметь смысл, если вы хотите рассматривать все три случая одинаково.

225
Daniel Earwicker

Вы можете перебирать свойства анонимного типа, используя Reflection; Посмотрите, есть ли свойство «Проверено», и если есть, то получите его значение.

Смотрите этот блог: http://blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in- hand.aspx

Так что-то вроде:

foreach(object o in nodes)
{
    Type t = o.GetType();

    PropertyInfo[] pi = t.GetProperties(); 

    foreach (PropertyInfo p in pi)
    {
        if (p.Name=="Checked" && !(bool)p.GetValue(o))
            Console.WriteLine("awesome!");
    }
}
12
glennkentwell

Принятый ответ правильно описывает, как должен быть объявлен список, и настоятельно рекомендуется для большинства сценариев.

Но я столкнулся с другим сценарием, который также охватывает заданный вопрос .. Что если вам нужно использовать существующий список объектов, например ViewData["htmlAttributes"] в MVC? Как вы можете получить доступ к его свойствам (они обычно создаются через new { @style="width: 100px", ... })?

Для этого немного другого сценария я хочу поделиться с вами тем, что я узнал. В приведенных ниже решениях я предполагаю следующее объявление для nodes:

List<object> nodes = new List<object>();

nodes.Add(
new
{
    Checked = false,
    depth = 1,
    id = "div_1" 
});

1. Решение с динамикой

В C # 4.0 и более поздних версиях вы можете просто привести к динамическому и написать:

if (nodes.Any(n => ((dynamic)n).Checked == false))
    Console.WriteLine("found not checked element!");

Примечание: При этом используется позднее связывание, что означает, что он распознает только во время выполнения, если у объекта нет свойства Checked, и в этом случае выдает RuntimeBinderException - поэтому, если вы пытаетесь использовать несуществующее свойство Checked2 вы получите следующее сообщение во время выполнения: "'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'".

2. Решение с отражением

Фон

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

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

if (nodes.AccessListItems().Any(n => (bool)n["Checked"] == false))
{
    Console.WriteLine("found not checked element!");
}

Все, что требуется, это добавить класс расширения ниже:

// simplifies access to anonymous properties
public static class AnonymousTypeExtensions
{
    // make properties of object accessible 
    // eg. x.AccessProperties() or x.AccessProperties()["PropName"]
    public static IDictionary AccessProperties(this object o, string propertyName=null)
    {
        Type type = o?.GetType();
        var properties = type?.GetProperties()
        ?.Select(n => n.Name)
        ?.ToDictionary(k => k, k => type.GetProperty(k).GetValue(o, null));
        return properties;
    }

    // returns specific property, i.e. x.AccessProperty(propertyName)
    public static object AccessProperty(this object o, string propertyName)
    {
        return o?.AccessProperties()?[propertyName];
    }

    // converts object list into list of properties
    public static List<IDictionary> AccessListItems(this List<object> objectList)
    {
        var accessibleList = new List<IDictionary>();
        foreach (object obj in objectList)
        {
            accessibleList.Add(obj.AccessProperties());
        }
        return accessibleList;
    }   
}

Приведенный выше код использует операторы null-conditional , доступные с версии C.0 версии 6.0 - если вы работаете со старыми компиляторами, просто замените ?. на . и ?[ на [. В противном случае оставьте все как есть, потому что это значительно упрощает обработку нуля.

Примечание: Как и в другом решении с динамическим, это решение также использует позднюю привязку, но в этом случае вы не получите исключение - он просто не найдет элемент, если вы ссылаетесь на несуществующее свойство , Что может быть полезно для некоторых приложений, так это то, что свойство указывается через строку.

0
Matt

Недавно у меня была такая же проблема в .NET 3.5 (динамическая недоступность). Вот как я решил:

// pass anonymous object as argument
var args = new { Title = "Find", Type = typeof(FindCondition) };

using (frmFind f = new frmFind(args)) 
{
...
...
}

Адаптировано где-то на stackoverflow:

// Use a custom cast extension
public static T CastTo<T>(this Object x, T targetType)
{
   return (T)x;
}

Теперь верните объект через приведение:

public partial class frmFind: Form
{
    public frmFind(object arguments)
    {

        InitializeComponent();

        var args = arguments.CastTo(new { Title = "", Type = typeof(Nullable) });

        this.Text = args.Title;

        ...
    }
    ...
}
0
orfruit