当前位置: 首页 > news >正文

C#基础06-函数异常

零、文章目录

C#基础06-函数异常

1、函数定义与语法

(1)定义与语法
[访问修饰符] [static] 返回类型 函数名(参数列表)
{// 函数体return 返回值; // 非void时必须 
}
  • 访问修饰符:public(全局可见)、private(类内访问)、protected(派生类可访问)
  • 静态修饰符:static 使函数无需实例化即可调用(如工具类方法)
  • 返回值:void 表示无返回值;其他类型需用 return 返回匹配的值
  • 参数列表:可包含零或多个参数,格式:类型 参数名
  • 案例
public static int Add(int a, int b) 
{return a + b; 
}
(2)参数传递机制
传递方式关键字内存行为特点调用示例
值传递传递原始数据的副本函数内修改不影响外部变量Add(x, y)
引用传递ref传递原始数据的内存地址函数内修改直接影响外部变量Swap(ref a, ref b)
输出参数out传递内存地址,必须赋值用于返回多个结果Parse(str, out int num)
  • 案例
// 值传递示例(外部num不变)
void Increment(int num) { num++; }// 引用传递示例(外部num改变)
void IncrementRef(ref int num) { num++; }// 输出参数示例(必须在函数内赋值)
void GetValues(out int x, out int y) { x = 1; y = 2; }
(3)可选参数与命名参数
  • 可选参数:为参数指定默认值
void Log(string msg, bool isError = false) { ... }
Log("Info"); // 使用默认值isError=false 
  • 命名参数:按参数名传递(增强可读性)
Log(msg: "Error", isError: true);
(4)返回值控制
  • void 函数所有分支必须返回匹配类型的值
  • 避免在返回前修改引用类型的状态(可能引发副作用)
(5)异常处理
  • 在函数内捕获可恢复错误,向上抛出严重错误:
public int SafeDivide(int a, int b)
{if (b == 0) throw new DivideByZeroException();return a / b;
}

2、ref 和 out 关键字

(1)ref :修改现有变量
  • 场景:交换两个变量的值(需直接修改原始变量)。
static void Swap(ref int a, ref int b)
{int temp = a;a = b;b = temp;
}static void Main()
{int x = 10, y = 20;Swap(ref x, ref y);Console.WriteLine($"x={x}, y={y}"); // 输出:x=20, y=10 
}
  • 关键点:
    • 调用前 xy 必须初始化(否则编译报错)。
    • 方法内可直接读取或修改参数值(非强制修改)。
(2)out :返回多个结果
  • 场景:尝试解析字符串为整数(需返回解析结果和成功状态)。
static bool TryParseInt(string input, out int result)
{if (int.TryParse(input, out int parsedValue)){result = parsedValue; // 必须赋值 return true;}result = 0; // 必须赋值(即使失败)return false;
}static void Main()
{string input = "123";int number; // 无需初始化 if (TryParseInt(input, out number))Console.WriteLine($"解析成功:{number}"); // 输出:123
}
  • 关键点:
    • 调用前 number 无需初始化。
    • 方法内 必须为 out 参数赋值(否则编译报错)。
(3)进阶:大型结构体优化性能
  • 场景:避免复制大型结构体(如修改配置)。
struct Config { public int Size; public string Name; }static void ModifyConfig(ref Config config)
{config.Size *= 2; // 直接修改原结构体 
}static void Main()
{Config cfg = new Config { Size = 100, Name = "Default" };ModifyConfig(ref cfg);Console.WriteLine($"New Size: {cfg.Size}"); // 输出:200 
}
  • 为何用 ref: 避免值类型结构体复制的性能开销。
(4)refout 核心区别总结
特性refout
初始化要求调用前必须初始化变量调用前无需初始化变量
方法内赋值可读可写(非强制赋值)必须赋值(否则编译错误)
典型场景修改现有变量、交换数据返回多个结果、解析类操作
示例Swap(ref a, ref b)TryParse(..., out result)
(5)注意事项
  • 重载限制:仅因 ref/out 不同的方法无法重载(编译器视为相同签名)。
  • 禁止场景:异步方法(async)中不能使用 ref/out 参数。
  • 替代方案:需返回多个值时,可考虑元组 (int, bool) 或自定义类。

3、params数组参数

(1)params 的核心功能
  • params :用于声明可变数量的方法参数,允许在调用方法时传递:
    • 同类型的多个独立参数
    • 一个数组
    • 或不传递任何参数(空数组)
// 定义使用 params 的方法
public int Sum(params int[] numbers) {int sum = 0;foreach (int num in numbers) sum += num;return sum;
}// 调用示例 
Sum(1, 2, 3);    // ✅ 直接传递多个整数 
Sum(new int[] {4, 5}); // ✅ 显式传递数组 
Sum();            // ✅ 空参数(返回0)
(2)关键语法规则与限制
  • 位置要求
    • params 参数必须是方法参数列表中的最后一个参数。
    • 错误示例:void Foo(params int[] nums, string name) ❌(编译错误)
  • 类型限制
    • 必须声明为一维数组(如 int[]string[]),不支持多维数组或集合类型(如 List<T>)。
  • 与其他修饰符冲突
    • 不可与 refoutin 关键字同时使用。
    • 错误示例:void Process(ref params int[] values)
  • 传递规则
    • 若实参为数组 → 按引用传递(修改形参会影响原数组)
    • 若实参为独立值 → 按值传递(生成临时数组副本)
(3)典型应用场景
  • 简化方法调用
// 无需手动创建数组 
Console.WriteLine(JoinWords("Hello", "world")); // 输出 "Hello world"static string JoinWords(params string[] words) => string.Join(" ", words);
  • 数学计算工具
public static double Average(params double[] values) {if (values.Length == 0) return 0;return values.Sum() / values.Length;
}
// 调用:Average(90.5, 85.0, 92.3)
  • 日志记录
void Log(params object[] messages) {foreach (var msg in messages) File.AppendAllText("log.txt", msg.ToString() + "\n");
}
// 调用:Log("Error:", DateTime.Now, "File not found");
(4)注意事项与性能优化
  • 空值处理:当参数为空时,params 数组长度为 0(非 null),无需额外判空:
if (numbers.Length == 0) // 安全检查 
  • 性能影响
    • 频繁调用含 params 的方法可能触发堆内存分配(临时数组)。
    • 优化方案:对高频调用的方法提供固定参数的重载版本:
// 避免 params 的装箱开销
public int Sum(int a, int b) => a + b; // 优先调用此重载
public int Sum(params int[] numbers) { ... }
  • 类型安全:所有参数必须与数组类型严格匹配:
Sum(1, "two"); // ❌ 编译错误(类型不一致)
(5)对比其他语言的类似特性
特性C# (params)Java (...)Python (*args)
语法params int[]int...*args
空参数允许(空数组)允许(空数组)允许(空元组)
类型安全编译时检查编译时检查运行时检查
参数位置必须最后必须最后可变位置后需命名参数

4、args命令行参数

(1)基本概念与访问方式
  • 传递机制
    • 命令行参数通过 Main 方法的 string[] args 参数接收,args 是字符串数组。
    • 索引规则:args[0] 对应第一个参数(不包含程序名本身)。
  • 参数读取示例
static void Main(string[] args)
{Console.WriteLine($"参数数量: {args.Length}");Console.WriteLine("参数列表:");foreach (string arg in args){Console.WriteLine(arg); // 逐行输出参数 }
}
  • 输入命令 MyApp.exe param1 "param with space" param3 输出:
参数数量: 3
参数列表:
param1
param with space 
param3 
  • 空格参数需用引号包裹,否则会被拆分为独立参数。
️(2)不同应用场景的实现
  • 控制台应用程序
    • 直接通过 args 访问参数(参考上述示例)。
  • WinForms 应用程序
    • 修改 Program.csMain 方法,将参数传递给窗体:
static void Main(string[] args)
{Application.Run(new MainForm(args)); // 传递参数给窗体构造函数 
}
- 窗体类接收参数:  
public MainForm(string[] cmdArgs)
{InitializeComponent();if (cmdArgs.Length > 0) label1.Text = $"首个参数: {cmdArgs[0]}";
}
(3)进阶参数解析方法
  • 自定义解析类
    • 适用于复杂参数(如 -key=value 格式),需自行拆分字符串:
var paramDict = new Dictionary<string, string>();
foreach (string arg in args)
{string[] parts = arg.Split('=');if (parts.Length == 2) paramDict[parts[0]] = parts[1];
}
  • 专用解析库推荐
    • 使用第三方库(如 CommandLineParser)自动化处理:
      • 支持短选项(-f)、长选项(--file)、默认值等高级特性。
      • 简化错误处理与帮助文档生成。
️(4)关键注意事项
  • 参数分隔规则:空格作为默认分隔符,需用双引号包裹含空格的参数(如 "file path")。
  • 特殊字符转义:引号或等号需用反斜杠转义(如 \"\=)。
  • 参数数量限制:系统对参数总长度有限制(Windows 默认为 8191 字符),超出会导致错误。
(5)典型应用场景
  • 文件路径传递
MyApp.exe -input="data.txt" -output="result.csv"
  • 配置模式切换
MyApp.exe --mode=debug --log-level=verbose

5、函数使用场景

(1)输入验证与解析
public bool TryParseInt(string input, out int value)
{if (int.TryParse(input, out int result)) {value = result;return true;}value = 0; // out参数必须赋值 return false;
}
(2)递归计算阶乘
public int Factorial(int n)
{if (n == 0) return 1;return n * Factorial(n - 1); // 递归调用 
}

6、异常处理

(1)核心处理机制
  • try-catch-finally 结构
    • try:包裹可能引发异常的代码块。
    • catch:捕获特定类型异常并处理,支持多个 catch 块(按异常派生程度从高到低排列)。上面没捕获的异常传到下面继续捕捉。
    • finally:无论是否发生异常都会执行,常用于释放资源(如关闭文件、数据库连接)。
try {// 可能出错的代码(如 int a = 10 / 0;)
}
catch (DivideByZeroException ex) 
{Console.WriteLine($"除零错误:{ex.Message}");
}
catch (Exception ex) // 兜底处理 
{Console.WriteLine($"未知错误:{ex.Message}");
}
finally {// 必执行代码(如 file.Close();)
}
  • throw 关键字
    • 主动抛出异常:throw new InvalidOperationException("操作无效");
    • 重新抛出异常(保留原始堆栈):throw; 而非 throw ex;
  • 执行流程
    • 异常发生时,try块中后续代码中止,直接跳转至匹配的catch块
    • finally块即使遇到return也会执行
(2)关键实践原则
  • 资源清理:使用 finallyusing 语句确保资源释放(如文件流、数据库连接)。
using (StreamReader sr = new StreamReader("file.txt"))
{// 操作文件
} // 自动调用 Dispose()
  • 异常筛选:通过 when 条件细化捕获逻辑:
catch (IOException ex) when (ex.Message.Contains("磁盘已满"))
{// 处理特定错误 
}
(3)常见内置异常类
异常类型触发场景
DivideByZeroException整数除零操作
NullReferenceException访问空对象成员
IndexOutOfRangeException数组索引越界
FileNotFoundException文件路径不存在
InvalidCastException无效类型转换
ArgumentNullException方法参数为 null
(4)自定义异常
  • 继承 System.ApplicationException(推荐)或 System.Exception
  • 包含三个构造函数(无参/字符串参数/含内部异常参数)
public class OrderException : ApplicationException {public OrderException() {}public OrderException(string msg) : base(msg) {}public OrderException(string msg, Exception inner) : base(msg, inner) {}
}
  • 抛出与使用
if (orderCount < 0) {throw new OrderException("订单数不能为负!");
}
(5)关键实践与注意事项
  • 最佳实践
    • 优先用逻辑判断(如 if)预防可预见的错误(如非空校验),而非依赖异常处理
    • 使用 using 语句自动释放资源(替代手动 finally)
    • 记录异常细节:ex.Message(错误信息)、ex.StackTrace(调用堆栈)
  • 避坑指南
    • ❌ 避免用异常控制正常流程(性能开销大)
    • ❌ 不在 finally 中抛出新异常(会掩盖原始错误)
    • ✅ 重新抛出异常时用 throw; 而非 throw ex;(保留原始堆栈)
http://www.dtcms.com/a/430912.html

相关文章:

  • PostgreSQL LIMIT 语句详解
  • 网站开发是什么部门wordpress 缩略图清理
  • Kubernetes网络策略实战:精准控制frontend与backend跨-tail通信
  • 关于制作网站收费标准网站的结构类型
  • 【word解析】从OLE到OMML:公式格式转换的挑战与解决方案
  • 云梦网站开发如何做好企业网站
  • 常德网站制作公司多少钱服务器出租
  • Python 2025:低代码开发与自动化编程新纪元
  • wordpress手机端网站模板建站程序下载
  • SQL 多表查询常用语法速查:INNER JOIN / LEFT JOIN / RIGHT JOIN
  • p2p网贷网站开发页面设计简单吗
  • Java SE “异常处理 + IO + 序列化”面试清单(含超通俗生活案例与深度理解)
  • Redis 数据库管理与通信基础
  • GameObject 常见类型详解 -- 运输工具(TRANSPORT)
  • Spring的事务管理机制
  • DAY22 XML、XML解析
  • Lazygi - 让git操作不再困难
  • sns社交网站建设东莞服务36招
  • 有那些方法推广网站可用的在线网页代理
  • 一种基于模型残差的密度聚类方法之二(电力线分股)
  • 基于Keil下多文件打包生成LIB库的具体步骤
  • php网站开发教学购物软件哪个更好更便宜
  • 中小企业网站开发长期做网站应该购买稳定的空间
  • 二叉树的递归层序遍历
  • 牛客算法基础noob58 无限长正整数排列字符串
  • ECharts 配置语法详解
  • 哪个网站做自媒体比较好华为网站建设的目标是否明确
  • 【机器学习】 在Jupyter Notebook 中如何指定Python环境
  • springboot海洋馆预约系统的设计与实现(代码+数据库+LW)
  • 精通C语言(1.内存函数)