簡體   English   中英

Enumerable.Range - 什么時候使用才有意義?

[英]Enumerable.Range - When does it make sense to use?

在編程時,決定何時使用 for 循環或 foreach 幾乎是本能的,但是選擇使用 Enumerable.Range 的決定因素或問題空間是什么?

當我們想要迭代一定次數(通過簡單數據類型)來計算/執行重復性任務時,會選擇For 循環

For Each是類似的,但是當我們想要迭代復雜對象列表以計算/執行重復性任務時選擇。

同樣,使用 Enumerable.Range 的決定因素是什么?

IEnumerable<int> squares = Enumerable.Range(4, 3).Select(x => x * x);

foreach是關於迭代現有的集合/集合。

Enumerable.Range用於生成集合/集合。 如果可以由Enumerable.Range生成,您通常不會想要編寫一個for循環來生成一個集合 - 您只會編寫更長的樣板代碼並且需要您分配某種存儲(例如List<int> ) 首先填充。

如前所述, Enumerable.Range 不是針對循環,而是創建范圍。 這使得 Linq 中的一個襯墊成為可能,而無需創建子集。 這種功能的另一個優點是,您甚至可以在 sub 語句中生成一個子范圍,這for和 lambda 而言並不總是可能的,因為在 lambda 中不可能產生 yield。

例如, SelectMany也可以使用 Enumerable.Range。 測試集:

class House
{
    public string Name { get; set; }
    public int Rooms;
}

    var houses = new List<House>
    {
        new House{Name = "Condo", Rooms = 3},
        new House{Name = "Villa", Rooms = 10}
    };

這個例子本身當然沒有多大價值,但為了獲得所有房間,Linq 中的實現將是:

   var roomsLinq = houses.SelectMany(h => Enumerable.Range(1, h.Rooms).Select(i => h.Name + ", room " + i));

通過迭代,它將需要 2 次迭代:

   var roomsIterate = new List<string>();
    foreach (var h in houses)
    {
        for (int i = 1; i < h.Rooms + 1; i++)
        {
            roomsIterate.Add(h.Name + ", room " + i);
        }
    }

您仍然可以說,第二個代碼更具可讀性,但這歸結為一般情況下是否使用 Linq。


因此,更進一步,我們想要一個所有房間的IEnumerable<IEnumerable<string>> (每所房子的房間字符串可枚舉)。

林克:

listrooms = houses.Select(h => Enumerable.Range(1, h.Rooms).Select(i => h.Name + ", room " + i));

但是現在,我們在使用迭代時需要 2 個集合:

    var list = new List<IEnumerable<string>>();
    foreach (var h in houses)
    {
        var rooms = new List<string>();
        for (int i = 1; i < h.Rooms + 1; i++)
        {
            rooms.Add(h.Name + ", room " + i);
        }
        list.Add(rooms);
    }

另一種情況,我認為 Linqs 和 lambdas 的一大優點是您可以將它們用作參數(例如用於注入目的),這可以通過 Enumerable.Range 以更簡單的方式實現。

例如,你有一個函數,它接受一個參數 roomgenerator

static IEnumerable<Furniture> CreateFurniture(Func<House,IEnumerable<string>> roomgenerator){
   //some house fetching code on which the roomgenerator is used, but only the first 4 rooms are used, so not the entire collection is used.
}

上面的房間迭代可以用 Enumerable.Range 返回,但迭代時必須首先創建房間的子集合,或者生成結果的單獨函數。 子集合有一個很大的缺點,即它總是被完全填充,即使只需要枚舉中的一項。 單獨的方法通常是矯枉過正的,因為它只需要用於單個參數,因此 Enumerable.Range 可以節省一天。

Enumerable.Range()是一個生成器,即它是一種生成某種類型的n項的簡單而強大的方法。

需要一個包含某個類的隨機數量實例的集合? 沒問題:

Enumerable.Range(1,_random.Next())
    .Select(_ => new SomeClass
    {
        // Properties
    });

您可以將Enumerable.Range視為檢索集合子集的工具。

如果集合的內容是值類型,則還為它們中的每一個創建一個新實例。

是的,您可以使用for循環,通過設置正確的迭代邊界來實現,但是這樣想,幾乎所有LINQ都可以通過其他方式實現(最終它只是一個庫)。

但它具有簡短、簡潔和清晰的語法,這就是它被如此廣泛使用的原因。

你的例子已經足夠好了。

我可以用 for 循環來做同樣的代碼來構建結果

基本上有兩種方法可以使用循環構建它:

// building a collection for the numbers first
List<int> numbers = new List();
for (int i = 4; i < 7; i++)
    numbers.Add(i);

IEnumerable<int> squares = numbers.Select(x => x * x);

// or building the result directly
List<int> squares = new List();
for (int i = 4; i < 7; i++)
    numbers.Add(i * i);

這兩種解決方案的問題是您實際上需要創建一個您添加的集合。 所以你在某處有一個數字集合。

但是看看Enumerable.Range解決方案: Enumerable.Range是一個生成器,它像所有其他 Linq 方法一樣返回一個IEnumerable並且只在迭代時創建事物 所以永遠不存在包含所有數字的集合。

回答

for更快

Enumerable.Range更具可讀性/更清晰

如果您正在處理大量數組,請使用for循環。 如果您正在處理較小的輸入,則首選Enumerable.Range - 選擇您的權衡。

性能測試

public static void Main(string[] args)
{
    var stopwatch = new Stopwatch();
    var size = 1000000;
    int[] arrRange;
    
    stopwatch.Start();
    arrRange = Enumerable.Range(0, size).ToArray();
    stopwatch.Stop();
    P(stopwatch, "Enumerable.Range");

    stopwatch.Reset();
    var arrFor = new int[size];
    stopwatch.Start();
    for (var i = 0; i < size; i++)
        arrFor[i] = i;
    stopwatch.Stop();
    P(stopwatch, "For");

    var arrWhile = new int[size];
    var iw = 0;
    stopwatch.Reset();
    stopwatch.Start();
    while (iw < size)
    {
        arrWhile[iw] = iw;
        iw++;
    }
    stopwatch.Stop();
    P(stopwatch, "while");

    stopwatch.Reset();
    stopwatch.Start();
    var arrRepeat = Enumerable.Repeat(1, size);
    stopwatch.Stop();
    P(stopwatch, "Enumerable.Repeat");
}

public static void P(Stopwatch s, string type)
{
    Console.WriteLine($"{type}: {s.Elapsed}");
}

這是這個程序的輸出(在 .NET 6.0 中)

Enumerable.Range: 00:00:00.0070907
For: 00:00:00.0036731
while: 00:00:00.0036175
Enumerable.Repeat: 00:00:00.0003927

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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