2015-02-24 2 views
1

У меня естьКак получить тип элемента и членов из списка объектов?

List<object> list = new List<object>(); 
while (myReader.Read()) 
{ 
    string arrKablan = myReader["arrK_Title"].ToString(); 
    string arrTotal = myReader["arrTotal"].ToString(); 
    string _title = myReader["MF_Title"].ToString(); 
    string _path = myReader["MF_Path"].ToString(); 
    int _level = Convert.ToInt32(myReader["MF_Level"].ToString()); 

    list.Add(new { title = _title, path = _path, kablanim = arrKablan, total = arrTotal, level = _level }); 
} 

мне нужно выбрать только пункты, где уровень == 1

я попытался

list = list.where(item => item.level == 1); 

, но я получаю сообщение об ошибке

'object' does not contain a definition for 'level' and no extension method 'level' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) 

я знаю, что компилятор может получить тип, чтобы он мог знать, что это «уровень». Как я могу достичь такого выбора, не определяя класс?

+4

Реальный вопрос будет, почему бы вам не * хочу * определить тип? – Crono

+0

Как интерфейс мне поможет? \ – eyalb

+0

Просто отредактировал комментарий. Забудьте о интерфейсах на данный момент. В противном случае мы могли бы зайти слишком далеко, опираясь на рисунок декоратора/обертки. – Crono

ответ

2

Я не могу понять, почему вы не просто сделать конкретный класс:

public class Foo 
{ 
    public string Title { get; set; } 
    public string Path { get; set; } 
    // etc, etc 
} 

Тогда

List<Foo> list = new List<Foo>(); 

while (myReader.Read()) 
{ 
    string arrKablan = myReader["arrK_Title"].ToString(); 
    string arrTotal = myReader["arrTotal"].ToString(); 
    string _title = myReader["MF_Title"].ToString(); 
    string _path = myReader["MF_Path"].ToString(); 
    int _level = Convert.ToInt32(myReader["MF_Level"].ToString()); 

    list.Add(new Foo { Title = _title, Path = _path, /* etc, etc */ }); 
} 

тогда вы звоните становится

list = list.Where(item => item.Level == 1).ToList(); 

(Примечание: дополнительно ToList вызова требуется сделать list назначения действительного)

-1

Следующая строка создает анонимный класс.

new { title = _title, path = _path, kablanim = arrKablan, total = arrTotal, level = _level }); 

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

+0

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

+0

Вы проверили как ответ полностью то же самое, что я сказал вам, и у моего ответа есть минусы, очень забавные :) – EngineerSpock

+0

Простите, но я не минус вас :) – eyalb

4

У вас есть два способа крепления этого:

  1. Используйте List<dynamic> вместо List<object>. Это отключит проверки типов. Недостаток: это отключит проверки типов. :-)

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

    var myList = (from drow in myDataTable.AsEnumerable() 
           select new { 
            kablanim = drow["arrK_Title"].ToString(), 
            total = drow["arrTotal"].ToString(), 
            ... 
           }).ToList(); 
    
+0

это лучший ответ, но я думаю, что я буду использовать @dav_i ответ. – eyalb

0

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

Например, вы можете получить доступ к одному из следующих элементов: var list = new List();

list.Add(new { field1 = "a", field2 = 2 }); 
    list.Add(new { field1 = "b", field2 = 3 }); 
    list.Add(new { field1 = "c", field2 = 4 }); 

    dynamic o = list[1]; 

    Console.WriteLine(o.field1); 
    Console.WriteLine(o.field2); 

Но вы должны знать, что динамическая функция имеет большие накладные расходы при доступе каждого члена.

Если вы действительно хотите использовать лямбды, вы можете переписать

list = list.where(item => item.level == 1); 

как этот

var filtered = list.Where(item => 
{ 
    dynamic ditem = item; 
    return ditem.Level == 1; 
}); 

, но это плохой подход.

Другой подход заключается в использовании отражения и перепишем это лямбда, как этот

var filtered = list.Where(item => 
{ 
    var field = item.GetType().GetField("level"); 
    return (int)field.GetValue(item) == 1; 
}); 

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

Также было бы лучше кэшировать объект FieldInfo вне цикла, если ваши анонимные объекты имеют одинаковый тип. Это может быть сделано как этот

var field = list.First().GetType().GetField("level"); 
var filtered = list.Where(item => (int)field.GetValue(item) == 1); 
1

Вы можете получить значение свойства анонимного класса, как это:

 var anon = new { Level = "level", Time = DateTime.Now }; 
     Type type = anon.GetType(); 

     var props = type.GetProperties(); 
     foreach (var propertyInfo in props) 
     { 
      if (propertyInfo.Name == "Level") 
      { 
       var x =propertyInfo.GetValue(anon); 

      } 
     } 

я не уверен, если это лучший способ для достижения этой цели, но это, безусловно, возможно.

0

По соображениям производительности Linq зависит от наличия метаданных во время компиляции. Явным образом объявляя List<object>, вы набрали элементы этого списка как object, который не имеет члена level.

Если вы хотите использовать Linq, как это, у вас есть два варианта.

  1. Объявите класс с членом уровня и использовать его для типа коллекции
  2. Объявляет интерфейс с элементом уровня и использовать его, чтобы бросить в выражении лямбды

Вариант 1 является предпочтительным подход. Обычно Linq используется с базой данных, а классы генерируются Visual Studio непосредственно из базы данных. Вот почему никто не жалуется на необходимость того, чтобы классы поставляли метаданные.

2

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

private T GetValue<T>(object obj, string property) 
{ 
    return (T)obj.GetType() 
       .GetProperties() 
       .Single(p => p.Name == property) 
       .GetValue(obj); 
} 

И называть это так:

var filteredList = list.Where(item => GetValue<int>(item, "level") == 1); 
Смежные вопросы