如何从对象列表中获取项类型和成员?

时间:2015-02-24 13:33:48

标签: c# asp.net

我有一个

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 });
}

我只需要选择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?) 

我知道编译器可以获得类型,这样他就可以知道它是什么&#34; level&#34;。 如何在不定义类的情况下实现这种选择?

7 个答案:

答案 0 :(得分: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();
    

答案 1 :(得分: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次呼叫

答案 2 :(得分: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);

答案 3 :(得分: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);

            }
        }

我不确定这是否是实现这一目标的最佳途径,但肯定是可能的。

答案 4 :(得分:0)

您正在将匿名类的对象添加到列表中。您可以仅在您已定义的方法中引用此匿名类字段,并且您应该避免将其添加到列表中,因为现在还有其他方式可以反射或动态访问该对象的字段。< / p>

例如,您可以访问以下元素之一: 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);

但是你应该知道,这个动态功能对每个成员访问都有很大的开销。

如果你真的想使用lambdas,你可以重写

list = list.where(item => item.level == 1);
像这样

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

但这是一个糟糕的方法。

另一种方法是使用反射并像这样重写这个lambda

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);

答案 5 :(得分:0)

出于性能原因,Linq依赖于在编译时可用的元数据。通过明确声明List<object>,您已将此列表的元素键入为object,但没有成员level

如果你想像这样使用Linq,你有两个选择。

  1. 使用级别成员声明一个类,并使用它来键入集合
  2. 使用级别成员声明一个接口,并使用它来强制转换为lambda表达式
  3. 选项1是首选方法。通常,Linq与数据库一起使用,并且Visual Studio直接从数据库生成类。这就是为什么没有人抱怨类需要提供元数据的原因。

答案 6 :(得分:-1)

以下行创建匿名类。

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

你无法将你的对象强制转换为有意义的东西。 对象没有这些属性。 您必须自己创建一个类并使用它。