2015-02-24 77 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 2015-02-24 13:35:51

+0

界面將如何幫助我?\ – eyalb 2015-02-24 13:37:49

+0

剛剛編輯了評論。暫時忘記接口。否則,我們可能會對裝飾器/包裝器模式做出太詳細的闡述。 – Crono 2015-02-24 13:41:35

回答

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

(注意額外的需要呼叫使list分配有效)

-1

以下行創建匿名類。

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

您無法將您的對象轉換爲任何有意義的東西。 對象沒有這些屬性。 你必須自己創建一個類並使用它。

+0

我知道,但這是一個我只會用一次的類,所以我更喜歡使用匿名類型。 – eyalb 2015-02-24 13:37:19

+0

你檢查答案完全相同的事情,我告訴你,我的回答有缺點,非常有趣:) – EngineerSpock 2015-02-26 07:38:37

+0

對不起,但我沒有減去你:) – eyalb 2015-02-26 08:00:13

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 2015-02-24 13:47:24

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

但是你應該知道,這個動態特性對每個成員訪問都有很大的開銷。

如果你真的想使用lambda表達式,你可以重寫

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