1. 程式人生 > >試試 IEnumerable 的 10 個小例子

試試 IEnumerable 的 10 個小例子

IEnumerable 介面是 C# 開發過程中非常重要的介面,對於其特性和用法的瞭解是十分必要的。本文將通過10個小例子,來熟悉一下其簡單的用法。

全是原始碼

以下便是這10個小例子,響應的說明均標記在註釋中。

每個以 TXX 開頭命名的均是一個示例。建議從上往下閱讀。

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using Xunit;
using Xunit.Abstractions;

namespace Try_More_On_IEnumerable
{
    public class EnumerableTests
    {
        private readonly ITestOutputHelper _testOutputHelper;

        public EnumerableTests(
            ITestOutputHelper testOutputHelper)
        {
            _testOutputHelper = testOutputHelper;
        }

        [Fact]
        public void T01普通的迴圈獲取偶數()
        {
            // 建立一個集合存放最終結果
            var result = new List<int>();
            for (var i = 0; i <= 10; i++)
            {
                // 如果是偶數的話,將數字放入集合中
                if (i % 2 == 0)
                {
                    result.Add(i);
                }
            }

            // 斷言最終的結果
            result.Should().Equal(0, 2, 4, 6, 8, 10);
        }

        [Fact]
        public void T02普通Enumerable()
        {
            // 通過靜態方法獲取偶數的Enumerable物件
            var result = GetNumber(10);

            // 斷言結果
            result.Should().Equal(0, 2, 4, 6, 8, 10);

            /**
             * 這種寫法比起 T01 的寫法少了一個集合的建立,通常來說是效能更佳的寫法
             */
        }

        /// <summary>
        /// 獲取小於等於max的偶數
        /// </summary>
        /// <param name="max"></param>
        /// <returns></returns>
        private static IEnumerable<int> GetNumber(int max)
        {
            for (var i = 0; i <= max; i++)
            {
                if (i % 2 == 0)
                {
                    // 使用 yield return 返回每次迭代的結果
                    yield return i;
                }
            }
        }

        [Fact]
        public void T02本地函式()
        {
            // 通過本地函式獲取結果
            var result = GetNumber(10);

            // 斷言結果
            result.Should().Equal(0, 2, 4, 6, 8, 10);

            /**
             * 使用本地函式獲取 Enumerable 物件。
             * 此處的效果和靜態方法類似。
             * 本地函式是 C# 7.0 才開始支援的語法特性。
             * 關於本地函式詳細內容可以參見:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/local-functions
             */
            IEnumerable<int> GetNumber(int max)
            {
                for (var i = 0; i <= max; i++)
                {
                    if (i % 2 == 0)
                    {
                        yield return i;
                    }
                }
            }
        }

        [Fact]
        public void T03分離條件()
        {
            /**
             * 使用 Where 過濾結果
             * 這樣的做法分離了“自增”邏輯和“判斷是偶數”的邏輯
             */
            var result = GetNumber(10)
                .Where(x => x % 2 == 0);

            result.Should().Equal(0, 2, 4, 6, 8, 10);

            // 迭代時只是進行了自增操作
            IEnumerable<int> GetNumber(int max)
            {
                for (var i = 0; i <= max; i++)
                {
                    yield return i;
                }
            }
        }

        [Fact]
        public void T04Linq產生數值()
        {
            // 系統內建了一個方法來獲取數字的自增序列,因此可以如此簡化
            var result = Enumerable
                .Range(0, 11)
                .Where(x => x % 2 == 0);

            result.Should().Equal(0, 2, 4, 6, 8, 10);
        }

        [Fact]
        public void T05輸出233()
        {
            // 輸出3個數字 233
            var result = Get233().Take(3);
            result.Should().Equal(2, 3, 3);

            // 輸出5個數字 23333
            result = Get233().Take(5);
            result.Should().Equal(2, 3, 3, 3, 3);

            IEnumerable<int> Get233()
            {
                // 第一次輸出 2
                yield return 2;
                while (true)
                {
                    // 後面都輸出 3
                    yield return 3;
                }
            }

            /**
             * 值得注意的是 while(true) 並不會導致程式陷入死迴圈
             * 因為 yield return 是採用按需供給的方式執行的。
             * 關於這點可以參考右側文章:https://www.cnblogs.com/SilentCode/p/5014068.html
             */
        }

        [Fact]
        public void T06獲取10個小於50的隨機數()
        {
            /**
             * 過濾隨機數中小於50的數字,只獲取其中10個結果
             */
            var result = GetRandom()
                .Where(x => x < 50)
                .Take(10)
                .ToArray();

            // 斷言
            result.Should().HaveCount(10);
            result.All(x => x < 50).Should().BeTrue();

            IEnumerable<int> GetRandom()
            {
                var random = new Random();
                // 不斷輸出 0-100 的隨機數
                while (true)
                {
                    yield return random.Next(0, 100);
                }
            }
        }

        [Fact]
        public void T07自動補足隨機數()
        {
            // 獲取3個數字
            var result1 = GetData().Take(3);

            // 3 個數字分別為 0,1,2
            result1.Should().Equal(0, 1, 2);

            // 獲取 10 個數字
            var result2 = GetData().Take(10).ToArray();

            // 前 5 個數字分別為 0,1,2,3,4
            result2.Take(5).Should().Equal(0, 1, 2, 3, 4);
            // 從第 5 個開始的數字都大於 10
            result2.Skip(5).Take(5).Should().Match(x => x.All(a => a > 10));

            /**
             * 獲取一組資料,前5個數字是 0,1,2,3,4 。後面繼續獲取則為隨機數
             */
            IEnumerable<int> GetData()
            {
                var staticData = new[] {0, 1, 2, 3, 4};
                foreach (var i in staticData)
                {
                    yield return i;
                }

                var random = new Random();
                // 不斷輸出 10-100 的隨機數
                while (true)
                {
                    yield return random.Next(10, 100);
                }
            }
        }

        [Fact]
        public void T08條件轉迴圈()
        {
            var book1 = GetReadingBook(DateTime.Parse("2019-08-30")).First();
            book1.Should().Be("每個週五都是快樂的一天");

            var book2 = GetReadingBook(DateTime.Parse("2016-02-29")).First();
            book2.Should().Be("四年一次的邂逅");

            var book3 = GetReadingBook(DateTime.Parse("2019-09-01")).First();
            book3.Should().BeSameAs("月老闆軟體開發小妙招三十二則");

            // 獲取給定時間需要閱讀的書籍名稱
            IEnumerable<string> GetReadingBook(DateTime time)
            {
                // 週五
                if (time.DayOfWeek == DayOfWeek.Friday)
                {
                    yield return "每個週五都是快樂的一天";
                }

                // 2月29日
                if (time.Date.Month == 2 && time.Date.Day == 29)
                {
                    yield return "四年一次的邂逅";
                }

                // 其他時間閱讀名著
                yield return "月老闆軟體開發小妙招三十二則";
            }
        }

        [Fact]
        public void T09獲取一個連結字串()
        {
            // 獲取一個當前可用的連結字串
            var conn = GetConnectionString().FirstOrDefault();
            conn.Should().Be("Source=賽博坦;UID=月x;Password=******");

            IEnumerable<string> GetConnectionString()
            {
                // 從一個檔案中獲取連結字串
                var connFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "conn.txt");
                if (File.Exists(connFilename))
                {
                    var fileContent = File.ReadAllText(connFilename);
                    yield return fileContent;
                }

                // 從配置檔案中讀取連結字串
                var dbConnectionString = ConfigurationManager.ConnectionStrings["db"]?.ConnectionString;
                if (!string.IsNullOrEmpty(dbConnectionString))
                {
                    yield return dbConnectionString;
                }

                // 返回預設的字串
                yield return "Source=賽博坦;UID=月x;Password=******";
            }
        }

        [Fact]
        public async Task T10測試網路連線()
        {
            var httpClient = new HttpClient();
            try
            {
                await Task.WhenAll(SendRequests());
                _testOutputHelper.WriteLine("當前網路連線正常");
            }
            catch (Exception e)
            {
                _testOutputHelper.WriteLine("當前網路不正常,請檢查網路連線");
            }

            IEnumerable<Task> SendRequests()
            {
                yield return Task.Run(() => httpClient.GetAsync("http://www.baidu.com"));
                yield return Task.Run(() => httpClient.GetAsync("http://www.bing.com"));
                yield return Task.Run(() => httpClient.GetAsync("http://www.taobao.com"));
            }
        }
    }
}

  

原始碼說明

以上示例的原始碼放置於部落格示例程式碼庫中。

專案採用 netcore 2.2 作為目標框架,因此需要安裝 netcore 2.2 SDK 才能執行。

  • 本文作者: Newbe36524
  • 本文連結: http://www.newbe.pro/2019/09/01/Others/Try-More-On-IEnumerable/
  • 版權宣告: 本部落格所有文章除特別宣告外,均採用 BY-NC-SA 許可協議。轉載請註明出處!