[英]Strange behaviour of OrderBy Linq
我有一個使用OrderBy()
Linq函數排序的列表,它返回一個IOrderedEnumerable
。
var testList = myList.OrderBy(obj => obj.ParamName);
ParamName是一個可以保存整數和字符串的對象。 上面的orderBy基於整數值對列表進行排序。 現在我在testList上運行foreach並根據其整數值將ParamName屬性更改為某個字符串,如下所示,
using (var sequenceEnum = testList.GetEnumerator())
{
while (sequenceEnum.MoveNext())
{
sequenceEnum.Current.ParamName = GetStringForInteger(int.Parse(Convert.ToString(sequenceEnum.Current.ParamName)));
}
}
接下來發生的事情是上一循環之后列表中項目的順序已被中斷,並且已根據分配的字符串而不是初始排序對列表進行排序。
但是,當我將.ToList()
與.OrderBy()
子句一起使用時,將.OrderBy()
。
有誰能幫助我這里發生的事情?
樣本輸出圖示:
編輯:我們都錯了你的問題。 錯誤排序的原因是因為你正在比較“B”和“AA”,並期望AA在excel之后的B之后,當然不會按字母順序發生。
在排序之前指定顯式比較器或在執行排序之前將ParamName轉換為Int。
Linq通常返回IEnumerable元素的原因是它具有惰性評估行為。 這意味着它將在您需要時評估結果,而不是在構建時評估結果。
調用ToList強制linq評估結果以生成預期列表。
TL; DR在執行linq查詢並在獲取結果之前更改源數據集時要非常小心。
原因是在EF中分離執行查詢,這意味着在您通過.ToList()顯式加載到內存之前,不會對DB進行實際查詢。
正如你所說的那樣.OrderBy()返回一個IOrderedEnumerable,它與foreach成語一起使用。 那么為什么不簡化它做類似下面的事情呢?
foreach(var item in testList)
{
item.ParamName = GetStringForInteger(int.Parse(Convert.ToString(item.ParamName)));
}
正如大家在這里提到的,那是因為Linq被懶惰地評估了。 你可以在這里閱讀更多內容: https : //blogs.msdn.microsoft.com/ericwhite/2006/10/04/lazy-evaluation-and-in-contrast-eager-evaluation/
你想做的可能是這樣的:
var testList = myList.OrderBy(obj => obj.ParamName).Select(obj =>
{
obj.ParamName = GetStringForInteger(int.Parse(Convert.ToString(obj.ParamName)));
return obj;
});
IEnumerable對象本身並不表示對象序列,它表示根據請求將序列的第一個元素作為“當前元素”提供給您的算法,並為您提供當前元素之后的下一個元素。
當LINQ的發明,它決定LINQ使用延遲執行的,經常被稱為懶評價的概念。 在使用延遲執行的可枚舉函數的MSDN描述中,您將找到以下短語:
此方法通過使用延遲執行來實現。 立即返回值是一個對象,它存儲執行操作所需的所有信息。 在通過直接調用其GetEnumerator方法或使用foreach枚舉對象之前,不會執行此方法表示的查詢。
如果創建IEnumerable,並更改IEnumerable對象所作用的對象,則此更改可能會影響結果。 如果函數作用的參數發生更改,則它與返回不同值的函數相當:
int x = 4;
int y = 5;
int MyFunction()
{
return x + y;
}
int a = MyFunction();
y = 7;
int b = MyFunction();
現在b不等於a。 與您的IEnumerable類似:
List<...> myList = CreateMySequence()
var IEnumerable<...> myOrder = myList.OrderBy(...);
myOrder不包含結果,但就像一個可以計算結果的函數。 如果更改myOrder使用的其中一個參數,結果可能會更改:
myList.Add(someElement);
var myResult = myOrder.ToList();
myResult已更改,因為您更改了該功能。
延遲執行被發明的原因是因為通常您不需要枚舉序列的所有元素。 在下列情況下,如果您創建完整的序列,那將是處理時間的浪費:
當然,只要您要求第一個元素,就需要創建完整序列的函數:
根據經驗,明智的做法是盡可能長時間地將所有序列保持為IEnumerable,直到您需要結果,或者直到用於創建序列的源被更改為止。
當從數據庫,文件和互聯網獲取數據時,后者非常重要:您必須在關閉連接之前創建序列。
以下不會奏效
using (var myDbContext = new MyDbContext)
{
return MyDbContext.Customers.Where(customer => customer.Age > 18);
}
在離開using語句時,在Disposed myDbContext之前不執行數據庫查詢。 因此,一旦您要求序列中的任何元素,您將獲得異常。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.