簡體   English   中英

OrderBy Linq的奇怪行為

[英]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已更改,因為您更改了該功能。

延遲執行被發明的原因是因為通常您不需要枚舉序列的所有元素。 在下列情況下,如果您創建完整的序列,那將是處理時間的浪費:

  • 我只想要第一個元素,
  • 我想跳過3個元素,然后取兩個元素,
  • 我想要第一個元素的值為x
  • 我想知道序列是否包含任何元素

當然,只要您要求第一個元素,就需要創建完整序列的函數:

  • 如果您想要排序序列中的第一個,則必須對所有元素進行排序以找到第一個元素。
  • 如果你想要一組元素的第一個元素,其中組中的所有元素都具有相同的屬性值X(Enumerable.GroupBy)

根據經驗,明智的做法是盡可能長時間地將所有序列保持為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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM