首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 以 out 參數回傳結構體陣列:資料庫分頁查詢實戰模式

     編輯:關於C語言

場景痛點

需從資料庫查詢使用者清單,以結構體陣列 TUSER[] 形式經由 out 參數回傳給呼叫端 (Web Form / API Controller),避免建立額外 DTO 類別或 Tuple 封裝。

資料庫結構

CREATE TABLE Users (
    UserID      INT IDENTITY PRIMARY KEY,
    LoginName   NVARCHAR(50) NOT NULL,
    LoginPass   NVARCHAR(100) NOT NULL, -- 實務應存雜湊
    TrueName    NVARCHAR(50),
    Email       NVARCHAR(100),
    Level       INT NOT NULL DEFAULT 0
);

結構體定義

public struct TUSER
{
    public string LoginName;
    public string LoginPass;
    public string TrueName;
    public string Email;
    public int Level;
}

資料存取類別 (ADO.NET 模式)

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;

public class UserRepository
{
    private readonly string _connectionString;

    public UserRepository(string connectionString)
    {
        _connectionString = connectionString;
    }

    /// 
    /// 取得所有使用者,以 out 參數回傳結構體陣列
    /// 
    /// 輸出:使用者結構體陣列
    /// true=成功/有資料;false=失敗/無資料
    public bool GetAllUsers(out TUSER[] users)
    {
        users = Array.Empty(); // 預設空陣列,避免 null

        const string sql = "SELECT LoginName, LoginPass, TrueName, Email, Level FROM Users ORDER BY UserID";

        // 使用 List 暫存,最後 ToArray()
        var tempList = new List();

        using (var conn = new SqlConnection(_connectionString))
        using (var cmd = new SqlCommand(sql, conn))
        {
            try
            {
                conn.Open();
                using (var reader = cmd.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        tempList.Add(new TUSER
                        {
                            LoginName = reader.GetString(0),
                            LoginPass = reader.GetString(1),
                            TrueName = reader.IsDBNull(2) ? "" : reader.GetString(2),
                            Email = reader.IsDBNull(3) ? "" : reader.GetString(3),
                            Level = reader.GetInt32(4)
                        });
                    }
                }

                if (tempList.Count == 0) return false; // 無資料

                users = tempList.ToArray();
                return true;
            }
            catch (Exception ex)
            {
                // 實務:記錄 Log (ex.ToString())
                return false;
            }
        }
    }
}

呼叫端使用範例 (Web Forms / MVC Controller)

// Web Forms Button Click
protected void btnLoadUsers_Click(object sender, EventArgs e)
{
    var repo = new UserRepository(ConfigurationManager.ConnectionStrings["Default"].ConnectionString);

    if (repo.GetAllUsers(out TUSER[] users))
    {
        rptUsers.DataSource = users;
        rptUsers.DataBind();
        lblMessage.Text = $"載入 {users.Length} 筆資料";
    }
    else
    {
        lblMessage.Text = "查無資料或發生錯誤";
    }
}

// ASP.NET Core Controller
[HttpGet]
public IActionResult GetUsers()
{
    var repo = new UserRepository(_config.GetConnectionString("Default"));
    if (repo.GetAllUsers(out TUSER[] users))
        return Ok(users);
    return NotFound("No users found");
}

關鍵設計決策解析

1. 為何用 out TUSER[] 而非 ref 或回傳值?

  • out 語意明確:「此方法產出此陣列」,呼叫端無需預先初始化。
  • 回傳 TUSER[] 同樣可行,但 out 允許同時回傳 bool 狀態碼,避免 null 語意模糊 (失敗 vs 空陣列)。
  • 若採用現代模式,建議回傳 Result<TUSER[]> 或拋出異常,而非 bool+out。

2. 結構體 vs 類別

特性struct TUSERclass TUSER
記憶體值型別,陣列元素連續內嵌資料參考型別,陣列存參考,物件在堆疊
複製行為指派即複製所有欄位指派複製參考
null 可能性不可為 null (陣列元素永遠存在)可為 null
適用場景小型、不可變、高頻配置 (如 DTO)大型、可變、需繼承/多型

本案例資料單純、唯讀傳輸,struct 效能更佳 (減少 GC 壓力、記憶體局部性好)。

3. 避免雙重查詢 (原始碼問題)

原始片段先 SELECT COUNT(*) 再查資料,**完全多餘**。
List<T> 動態擴容機制已能高效處理未知筆數,單次 ExecuteReader 即可。

4. 資源管理鐵律

  • using 區塊包裹 SqlConnection, SqlCommand, SqlDataReader → 確保釋放連線池資源。
  • **嚴禁** 手動 Open()/Close() 交錯,極易導致連線洩漏。

進階擴充:分頁查詢版本

public bool GetUsersPaged(int pageIndex, int pageSize, out TUSER[] users, out int totalCount)
{
    users = Array.Empty();
    totalCount = 0;

    // 1. 取總筆數
    string countSql = "SELECT COUNT(*) FROM Users";
    // 2. 分頁查詢 (SQL Server 2012+ OFFSET/FETCH)
    string pageSql = @"
        SELECT LoginName, LoginPass, TrueName, Email, Level
        FROM Users
        ORDER BY UserID
        OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY";

    using (var conn = new SqlConnection(_connectionString))
    {
        conn.Open();
        // 總筆數
        using (var countCmd = new SqlCommand(countSql, conn))
        {
            totalCount = (int)countCmd.ExecuteScalar();
        }
        // 分頁資料
        if (totalCount == 0) return false;

        using (var cmd = new SqlCommand(pageSql, conn))
        {
            cmd.Parameters.AddWithValue("@Offset", (pageIndex - 1) * pageSize);
            cmd.Parameters.AddWithValue("@PageSize", pageSize);
            using (var reader = cmd.ExecuteReader())
            {
                var list = new List();
                while (reader.Read())
                {
                    list.Add(new TUSER { /* ...同前... */ });
                }
                users = list.ToArray();
            }
        }
    }
    return users.Length > 0;
}

總結

out T[] 模式在舊版 ASP.NET / WinForms 專案中常見,能以最小改動回傳陣列資料。現代開發建議改用 泛型集合回傳值 (List<TUSER> / IEnumerable<TUSER>) 或 Result 模式,語意更清晰、支援 LINQ 鏈式操作、配合 Dependency Injection 更易測試。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved