九、参数(parameter)

CLR #参数

✅ 第九章:参数


📌 1. 可选参数(Optional Parameters)

  • 可以为参数指定默认值,调用时可以省略这些参数。
void Greet(string name = "Guest") {
    Console.WriteLine($"Hello, {name}");
}
Greet();           // 输出 Hello, Guest
Greet("Alice");    // 输出 Hello, Alice

默认值必须是编译时常量


📌 2. 命名参数(Named Parameters)

  • 使用参数名进行调用,可以改变参数顺序
void Info(string name, int age) { ... }
Info(age: 30, name: "Tom");

✅ 提高可读性,在调用多个可选参数的方法时非常有用。


📌 3. 隐式类型局部变量(var

  • 编译器通过赋值推断变量类型:
var x = 100;        // x 是 int
var name = "Jeff";  // name 是 string

✅ 不能用于未初始化的变量。
⚠️ 不等于动态类型,var静态类型推断,编译时确定。


📌 4. 引用传参(refout

ref(引用传递)

  • 调用方法前变量必须已赋值,方法可以修改它的值。
void Add(ref int x) { x += 10; }

out(输出参数)

  • 调用方法前变量不需赋值,但方法中必须赋值
void Init(out int x) { x = 100; }

out 适合返回多个结果;ref 适合传入-传出模式。


📌 5. 可变参数(params

  • 参数数量不确定时使用,必须是最后一个参数
void Print(params int[] nums) {
    foreach (int n in nums) Console.WriteLine(n);
}
Print(1, 2, 3);  // 输出 1 2 3

📌 6. 参数设计规范

  • 避免使用大量参数(超过 4~5 个考虑封装为对象)
  • 避免使用 out / ref,改为返回 TupleValueTuple
  • 命名参数和可选参数应搭配使用以提升可维护性
  • 优先使用 var 让代码更简洁,但不要牺牲可读性

🚫 传统写法:使用 out 参数

//方法:从数据库查询信息,并返回状态码 & 错误信息
public bool TryGetUser(int userId, out string name, out string error) {
    if (userId == 1) {
        name = "Alice";
        error = null;
        return true;
    } else {
        name = null;
        error = "User not found";
        return false;
    }
}

❌ 存在的问题:

  1. 返回值和逻辑混杂bool 仅表示成功,真正有用的值通过 out 拆散。
  2. 难以测试和模拟(单元测试中无法轻易模拟多个输出值)
  3. IDE 支持差,特别是在调试时必须手动看每个输出参数。

✅ 推荐写法一:使用 Tuple(C# 4.0+)

public Tuple<bool, string, string> TryGetUser(int userId) {
    if (userId == 1)
        return Tuple.Create(true, "Alice", null);
    else
        return Tuple.Create(false, null, "User not found");
}

// 调用
var result = TryGetUser(1);
bool success = result.Item1;
string name = result.Item2;
string error = result.Item3;

✅ 更清晰,但 命名语义差Item1, Item2) —— 建议使用 ValueTuple

✅ 推荐写法二:使用 ValueTuple(C# 7.0+)

public (bool success, string name, string error) TryGetUser(int userId) {
    if (userId == 1)
        return (true, "Alice", null);
    else
        return (false, null, "User not found");
}

// 调用
var (success, name, error) = TryGetUser(1);

✅ 优势:

  • 命名明确,易读性强
  • 返回结构可以传递、链式组合
  • 易于测试与模拟(返回对象可断言)

📌 7. 常量(const)和只读字段(readonly)的区别

特性 const readonly
值设置 编译期确定 构造函数中设置
类型限制 仅限基础类型 可为任何类型
版本兼容 需要重新编译引用程序集 引用程序集不需重新编译

✅ 常量用于真正不变的数据(如 PI = 3.14);
readonly 适合初始化后不可变但值可能随构造变化的成员(如时间戳)


🔄 Mermaid 图示:参数机制总结

graph TD A[方法参数] --> B[可选参数] A --> C[命名参数] A --> D[ref/out] A --> E[params] A --> F[var 隐式变量] A --> G[参数设计规范]

🧠 面试题(≥ 5 道含解析)


1️⃣ refout 有何区别?

  • ref:传入前必须赋值,方法中可以修改。
  • out:传入时可以未赋值,方法中必须赋值。

2️⃣ params 有哪些限制?

  • 只能有一个 params 参数,且必须是最后一个参数。
  • 不支持与 ref/out 结合使用。

3️⃣ var 是动态类型吗?

❌ 不是。 ✅ var静态类型推断,在编译时由编译器推断真实类型。


4️⃣ 为什么尽量避免过多使用 ref/out

  • 会增加方法的副作用,降低可读性与测试性。
  • 推荐使用元组(C# 7.0 起支持)或返回对象封装多个值。

5️⃣ const 为什么不适合跨程序集共享?

因为 const 的值在编译期就已嵌入代码中,更新常量值不会影响引用该常量的程序集。应使用 static readonly 替代。


✅ 总结

内容 要点
optional 默认值参数,提升接口友好度
named 提升调用代码可读性
ref/out 传引用,ref 需初始化,out 需方法中赋值
params 接收变长参数列表,编译为数组
var 编译器推断静态类型,非动态类型
const/readonly const 编译期常量,readonly 构造期常量
posted @ 2025-08-26 10:08  世纪末の魔术师  阅读(176)  评论(0)    收藏  举报