1. 方法重载:参数差异的艺术
1.1 基础语法与参数规则
public class Calculator
{
// === 重载规则:参数类型/数量不同 ===
public int Add(int a, int b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}
public int Add(int a, int b, int c)
{
return a + b + c;
}
}
// === 调用示例 ===
var calc = new Calculator();
Console.WriteLine(calc.Add(1, 2)); // 输出:3(int)
Console.WriteLine(calc.Add(1.5, 2.5)); // 输出:4.0(double)
Console.WriteLine(calc.Add(1, 2, 3)); // 输出:6(int)
注释:
- 返回类型不影响重载,但参数列表必须不同;
- 参数顺序不同(如
Add(int a, double b)
和Add(double a, int b)
)不算重载!
1.2 进阶技巧:可选参数与命名参数
public class Logger
{
// === 可选参数(默认值) ===
public void Log(string message, int level = 1)
{
Console.WriteLine($"Level {level}: {message}");
}
// === 命名参数(调用时指定参数名) ===
public void Log(string message, string category, bool isCritical)
{
Console.WriteLine(
$"Category: {category}, Critical: {isCritical}, Message: {message}"
);
}
}
// === 调用示例 ===
var logger = new Logger();
logger.Log("Error occurred"); // 输出:Level 1: Error occurred
logger.Log(category: "Security", // 命名参数
isCritical: true,
message: "Unauthorized access");
// 输出:Category: Security, Critical: True, Message: Unauthorized access
注释:
- 可选参数需在末尾,且默认值只能在定义时设置;
- 命名参数可打破参数顺序的限制,提升可读性。
1.3 泛型与重载的结合
public class GenericCalculator
{
// === 泛型约束(要求类型实现IComparable) ===
public T Max<T>(T a, T b) where T : IComparable
{
return a.CompareTo(b) > 0 ? a : b;
}
// === 重载:处理数组参数 ===
public T Max<T>(T[] array) where T : IComparable
{
T max = array[0];
foreach (T item in array)
{
if (item.CompareTo(max) > 0) max = item;
}
return max;
}
}
// === 调用示例 ===
var gc = new GenericCalculator();
Console.WriteLine(gc.Max(5, 10)); // 输出:10
Console.WriteLine(gc.Max(new int[] { 3, 1, 4 })); // 输出:4
注释:
- 泛型约束确保类型支持比较操作;
- 通过重载实现单个方法名处理不同参数类型。
2. 递归:自我调用的艺术
2.1 基础递归:阶乘与斐波那契
public class MathHelper
{
// === 阶乘递归(基本情况:n=0或1) ===
public int Factorial(int n)
{
if (n <= 1) return 1; // 终止条件
return n * Factorial(n - 1); // 递归步骤
}
// === 斐波那契数列(优化版:记忆化缓存) ===
private Dictionary<int, int> _cache = new();
public int Fibonacci(int n)
{
if (n <= 1) return n;
if (_cache.TryGetValue(n, out int result)) return result;
result = Fibonacci(n - 1) + Fibonacci(n - 2);
_cache[n] = result;
return result;
}
}
// === 调用示例 ===
var helper = new MathHelper();
Console.WriteLine(helper.Factorial(5)); // 输出:120
Console.WriteLine(helper.Fibonacci(10)); // 输出:55(优化后效率大幅提升)
注释:
- 未优化的斐波那契递归时间复杂度为O(2^n),通过缓存可降至O(n);
- 递归必须有终止条件,否则会栈溢出(StackOverflowException)。
2.2 递归与数据结构:二叉树遍历
public class TreeNode
{
public int Value { get; set; }
public TreeNode Left { get; set; }
public TreeNode Right { get; set; }
public TreeNode(int value) => Value = value;
}
public class TreeTraversal
{
// === 前序遍历(根-左-右) ===
public void PreOrder(TreeNode root)
{
if (root == null) return;
Console.Write(root.Value + " "); // 处理根节点
PreOrder(root.Left); // 递归左子树
PreOrder(root.Right); // 递归右子树
}
// === 后序遍历(左-右-根) ===
public void PostOrder(TreeNode root)
{
if (root == null) return;
PostOrder(root.Left);
PostOrder(root.Right);
Console.Write(root.Value + " ");
}
}
// === 构建二叉树并调用 ===
var root = new TreeNode(1)
{
Left = new TreeNode(2) { Left = new TreeNode(4), Right = new TreeNode(5) },
Right = new TreeNode(3) { Left = new TreeNode(6), Right = new TreeNode(7) }
};
var traversal = new TreeTraversal();
traversal.PreOrder(root); // 输出:1 2 4 5 3 6 7
traversal.PostOrder(root); // 输出:4 5 2 6 7 3 1
注释:
- 递归天然适合树结构的深度优先遍历(DFS);
- 通过改变处理根节点的顺序,可实现不同遍历方式。
3. 重载与递归的协同:高级场景
3.1 重载支持的递归函数
public class RecursiveCalculator
{
// === 递归求和(基础版) ===
public int Sum(params int[] numbers)
{
if (numbers.Length == 0) return 0;
return numbers[0] + Sum(numbers.Skip(1).ToArray()); // 递归调用
}
// === 重载:支持链式调用 ===
public int Sum(int first, int second) => first + second;
public int Sum(int first, int second, int third)
=> first + second + third;
}
// === 调用示例 ===
var rc = new RecursiveCalculator();
Console.WriteLine(rc.Sum(1, 2, 3)); // 输出:6(直接调用重载)
Console.WriteLine(rc.Sum(new[] { 1, 2, 3, 4 })); // 输出:10(递归分解)
注释:
params
关键字允许变长参数,结合递归实现任意长度数组求和;- 重载方法简化了常见参数组合的调用。
3.2 递归与泛型的结合
public class GenericTree<T> where T : IComparable<T>
{
public T Value { get; set; }
public GenericTree<T> Left { get; set; }
public GenericTree<T> Right { get; set; }
public GenericTree(T value) => Value = value;
}
public class TreeOperations<T> where T : IComparable<T>
{
// === 递归查找元素 ===
public bool Contains(GenericTree<T> root, T value)
{
if (root == null) return false;
if (root.Value.CompareTo(value) == 0) return true;
return Contains(root.Left, value) || Contains(root.Right, value);
}
// === 重载:支持默认值 ===
public bool Contains(GenericTree<T> root)
=> Contains(root, default(T)); // 默认查找默认值
}
// === 使用示例 ===
var tree = new GenericTree<string>("A")
{
Left = new GenericTree<string>("B"),
Right = new GenericTree<string>("C")
};
var operations = new TreeOperations<string>();
Console.WriteLine(operations.Contains(tree, "B")); // 输出:True
注释:
- 泛型约束确保类型支持比较操作;
- 重载方法允许灵活调用,默认参数简化了常见场景。
4. 高级技巧与常见问题
4.1 重载决策机制的陷阱
public class Human
{
public void Write(char value)
{
Console.WriteLine("char: " + value);
}
}
public class Male : Human
{
// === 重载而非重写 ===
public void Write(int value)
{
Console.WriteLine("int: " + value);
}
}
// === 调用示例 ===
var male = new Male();
male.Write('a'); // 输出:int:97!而非父类的char:a
// === 原因 ===
// 重载决策优先选择子类的方法,即使参数类型不匹配!
// 'a'的隐式转换为int(97)导致调用Male.Write(int)
注释:
- 子类的重载方法会隐藏父类同名方法,需用
new
关键字显式声明;- 正确写法:
public new void Write(char value)
。
4.2 递归的优化与替代方案
// === 尾递归优化(C#不支持,需手动改写为迭代) ===
public int FactorialIterative(int n)
{
int result = 1;
for (int i = 1; i <= n; i++)
{
result *= i;
}
return result;
}
// === 使用表达式树(Expression Tree)实现递归 ===
public class ExpressionTreeExample
{
public static Func<int, int> CreateFactorialFunc()
{
var n = Expression.Parameter(typeof(int), "n");
var one = Expression.Constant(1);
var condition = Expression.LessThanOrEqual(n, one);
var body = Expression.Condition(
condition,
one,
Expression.Multiply(n, Expression.Invoke(CreateFactorialFunc(), Expression.Subtract(n, one)))
);
return Expression.Lambda<Func<int, int>>(body, n).Compile();
}
}
注释:
- 尾递归在C#中无法优化,需手动改写为循环;
- 表达式树可用于动态生成递归逻辑,但复杂度较高。
5. 企业级实战案例
5.1 动态配置解析(重载+反射)
public class ConfigParser
{
// === 重载支持不同格式 ===
public void Parse(string configPath) => Parse(File.ReadAllText(configPath));
public void Parse(string configContent)
{
// 解析逻辑
}
// === 反射调用重载方法 ===
public void ParseDynamic(Type configType, params object[] args)
{
var method = GetType().GetMethod(
nameof(Parse),
BindingFlags.Instance | BindingFlags.Public
);
method.Invoke(this, args); // 根据参数类型选择重载
}
}
// === 调用示例 ===
var parser = new ConfigParser();
parser.Parse("config.json"); // 路径参数
parser.Parse("{ \"key\": \"value\" }"); // 内容参数
parser.ParseDynamic(typeof(string), "fallback.json"); // 动态调用
5.2 二叉树序列化(递归+泛型)
public class TreeSerializer<T>
{
public string Serialize(TreeNode<T> root)
{
if (root == null) return "null";
return $"[{root.Value}, {Serialize(root.Left)}, {Serialize(root.Right)}]";
}
public TreeNode<T> Deserialize(string data)
{
// 反序列化逻辑(递归构建树)
return null; // 省略实现细节
}
}
// === 使用示例 ===
var serializer = new TreeSerializer<int>();
var serialized = serializer.Serialize(root); // 输出:[1, [2, [4...]]]
6. 常见问题与解决方案
6.1 重载方法无法调用?
// 错误:参数类型不匹配
public void Print(int value) { ... }
Print("Hello"); // 编译错误
// 解决:添加重载或使用可选参数
public void Print(string value) { ... }
6.2 递归导致栈溢出?
// 避免大深度递归,改用迭代
public int FactorialIterative(int n)
{
int result = 1;
for (int i = 1; i <= n; i++) result *= i;
return result;
}
6.3 泛型约束冲突?
// 错误:无法约束为多个接口
public void Process<T>(T item) where T : IComparable, ICloneable { ... }
// 解决:确保类型同时实现多个接口
微知著。”*
通过以上6大核心技术,你已掌握C#方法重载与递归的“全场景武器库”:
- 方法重载:通过参数差异实现“一接口多实现”,提升代码复用性;
- 递归:将复杂问题分解为简单子问题,优雅解决树遍历、分治算法等场景;
- 高级技巧:结合泛型、反射、表达式树实现更灵活的设计。