C#学习日记3 异常处理,为什么要使用异常,枚举类型
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了C#学习日记3 异常处理,为什么要使用异常,枚举类型,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含10643字,纯文字阅读大概需要16分钟。
内容图文
![C#学习日记3 异常处理,为什么要使用异常,枚举类型](/upload/InfoBanner/zyjiaocheng/597/12d654b4604146f0b8f3a14af6f669a1.jpg)
异常处理
概念
程序中的运行时错误通过使用一种称为“异常”的机制在程序中传播。 异常由遇到错误的代码引发,由能够更正错误的代码捕捉。 异常可由 .NET 运行时或由程序中的代码引发。 一旦引发了一个异常,此异常会在调用堆栈中传播,直到找到针对它的 catch
语句。 未捕获的异常由系统提供的通用异常处理程序处理,该处理程序会显示一个对话框。
基本操作知识
如果引发异常的语句不在 try
块内或者包含该语句的 try
块没有匹配的 catch
块,则运行时将检查调用方法中是否有 try
语句和 catch
块。 运行时将继续调用堆栈,搜索兼容的 catch
块。 在找到并执行 catch
块之后,控制权将传递给 catch
块之后的下一个语句。
一个 try
语句可包含多个 catch
块。 将执行第一个能够处理该异常的 catch
语句;将忽略任何后续的 catch
语句,即使它们是兼容的也是如此。 Catch 块应始终按从最具有针对性(或派生程度最高)到最不具有针对性的顺序排列。 例如:
执行 catch
块之前,运行时会检查 finally
块。 Finally
块使程序员可以清除中止的 try
块可能遗留下的任何模糊状态,或者释放任何外部资源(例如图形句柄、数据库连接或文件流),而无需等待垃圾回收器在运行时完成这些对象。
finally/catch都可以和try单独使用
C# 中的异常类
C# 异常是使用类来表示的。C# 中的异常类主要是直接或间接地派生于 System.Exception 类。System.ApplicationException 和 System.SystemException 类是派生于 System.Exception 类的异常类。
System.ApplicationException 类支持由应用程序生成的异常。所以程序员定义的异常都应派生自该类。
System.SystemException 类是所有预定义的系统异常的基类。下面的一些就是
异常类 | 描述 |
---|---|
System.IO.IOException | 处理 I/O 错误。 |
System.IndexOutOfRangeException | 处理当方法指向超出范围的数组索引时生成的错误。 |
System.ArrayTypeMismatchException | 处理当数组类型不匹配时生成的错误。 |
System.NullReferenceException | 处理当依从一个空对象时生成的错误。 |
System.DivideByZeroException | 处理当除以零时生成的错误。 |
System.InvalidCastException | 处理在类型转换期间生成的错误。 |
System.OutOfMemoryException | 处理空闲内存不足生成的错误。 |
System.StackOverflowException | 处理栈溢出生成的错误。 |
创建用户自定义异常
[Serializable]
public class InvalidDepartmentException : Exception
{
public InvalidDepartmentException() : base() { }
public InvalidDepartmentException(string message) : base(message) { }
public InvalidDepartmentException(string message, Exception inner) : base(message, inner) { }
// A constructor is needed for serialization when an
// exception propagates from a remoting server to the client.
protected InvalidDepartmentException(System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}
将异常详细化
能够很好地理解可能会引发异常的原因,并且可以实现特定的恢复,例如捕获 FileNotFoundException 对象时提示用户输入新文件名。可以创建和引发一个新的、更具体的异常。
int GetInt(int[] array, int index)
{
try
{
return array[index];
}
catch (IndexOutOfRangeException e)
{
throw new ArgumentOutOfRangeException(
"Parameter index is out of range.", e);
}
}
部分处理异常
想要先对异常进行部分处理,然后再将其传递以进行额外处理。 在下面的示例中,catch
块用于在重新引发异常之前将条目添加到错误日志。
try
{
// Try to access a resource.
}
catch (UnauthorizedAccessException e)
{
// Call a custom error logging procedure.
LogError(e);
// Re-throw the error.
throw;
}
异常筛选器
int GetInt(int[] array, int index)
{
try
{
return array[index];
}
catch (IndexOutOfRangeException e) when (index < 0)
{
throw new ArgumentOutOfRangeException(
"Parameter index cannot be negative.", e);
}
catch (IndexOutOfRangeException e)
{
throw new ArgumentOutOfRangeException(
"Parameter index cannot be greater than the array size.", e);
}
}
始终返回 false
的异常筛选器可用于检查所有异常,但不可用于处理异常。 典型用途是记录异常:
public static void Main()
{
try
{
string? s = null;
Console.WriteLine(s.Length);
}
catch (Exception e) when (LogException(e))
{
}
Console.WriteLine("Exception must have been handled");
}
private static bool LogException(Exception e)
{
Console.WriteLine($"\tIn the log routine. Caught {e.GetType()}");
Console.WriteLine($"\tMessage: {e.Message}");
return false;
}
什么情况下要用异常
首先为什么要用异常,而不是用if(JAVA 白学了)
主要 目的是为了区分正常情况和异常情况,就像我写的毕业设计那样,正常情况和异常情况都返回success。然后在前端无法做到统一判断,只能逐一判断,傻逼得很。
下面的话不明觉厉
我在写一个从数据库查询衣服价格的函数时,我需要考虑数据库超时的问题吗?我当然不应该,也不能考虑,因为这两个逻辑差太多了吧,就不该写在一起。但超时这个事情一旦发生了,由必须由谁来处理一下,总不能傻等或听之任之吧。
实际上Java的处理方式很简单,声明一个throws SQLException就行了。数据库超时问题由最外层的函数去处理吧,咱们里面的函数专注业务就行了。
方法无法完成其定义的功能。
例如,如果一种方法的参数具有无效的值
static void CopyObject(SampleClass original)
{
_ = original ?? throw new ArgumentException("Parameter cannot be null", nameof(original));
}
根据对象的状态,对某个对象进行不适当的调用。
一个示例可能是尝试写入只读文件。 在对象状态不允许操作的情况下,引发 InvalidOperationException 的实例或基于此类的派生的对象。 以下代码是引发 InvalidOperationException 对象的方法示例:
public class ProgramLog
{
FileStream logFile = null!;
public void OpenLog(FileInfo fileName, FileMode mode) { }
public void WriteLog()
{
if (!logFile.CanWrite)
{
throw new InvalidOperationException("Logfile cannot be read-only");
}
// Else write data to the log and return.
}
}
方法的参数引发了异常。
在这种情况下,应捕获原始异常,并创建 ArgumentException 实例。 应将原始异常作为 InnerException 参数传递给 ArgumentException 的构造函数:
static int GetValueFromArray(int[] array, int index)
{
try
{
return array[index];
}
catch (IndexOutOfRangeException ex)
{
throw new ArgumentException("Index is out of range", nameof(index), ex);
}
}
引发异常时应避免的情况
以下列表标识了引发异常时要避免的做法:
- 不要使用异常在正常执行过程中更改程序的流。 使用异常来报告和处理错误条件。
- 只能引发异常,而不能作为返回值或参数返回异常。
- 请勿有意从自己的源代码中引发 System.Exception、System.SystemException、System.NullReferenceException 或 System.IndexOutOfRangeException。
- 不要创建可在调试模式下引发,但不会在发布模式下引发的异常。 若要在开发阶段确定运行时错误,请改用调试断言。
注意点
- 异常包含一个名为 StackTrace 的属性。 此字符串包含当前调用堆栈上的方法的名称,以及为每个方法引发异常的位置(文件名和行号)。 StackTrace 对象由公共语言运行时 (CLR) 从
throw
语句的位置点自动创建,因此必须从堆栈跟踪的开始点引发异常。 - 所有异常都包含一个名为 Message 的属性。 应设置此字符串来解释发生异常的原因。 不应将安全敏感的信息放在消息文本中。 除 Message 以外,ArgumentException 也包含一个名为 **ParamName** 的属性,应将该属性设置为导致引发异常的参数的名称。 在属性资源库中,ParamName 应设置为
value
。 - 公共的受保护方法在无法完成其预期功能时将引发异常。 引发的异常类是符合错误条件的最具体的可用异常。 这些异常应编写为类功能的一部分,并且原始类的派生类或更新应保留相同的行为以实现后向兼容性。
枚举类型
介绍
enum Season
{
Spring,
Summer,
Autumn,
Winter
}
默认情况下,枚举成员的关联常数值为类型 int
;它们从零开始**,并按定义文本顺序递增 1**。 可以显式指定任何其他整数数值类型作为枚举类型的基础类型。 还可以显式指定关联的常数值,如下面的示例所示:
枚举类型 E
的默认值是由表达式 (E)0
生成的值,即使零没有相应的枚举成员也是如此(即如果你设置的不是从0开始,默认值还是会是0)
enum ErrorCode : ushort
{
None = 0,
Unknown = 1,
ConnectionLost = 100,
OutlierReading = 200
}
不能在枚举类型的定义内定义方法。 若要向枚举类型添加功能,请创建扩展方法。
作为位标志的枚举类型(有点难有点牛逼,位运算,其实不难)
如果希望枚举类型表示选项组合,请为这些选项定义枚举成员,以便单个选项成为位字段。 也就是说,这些枚举成员的关联值应该是 2 的幂。 然后,可以使用按位逻辑运算符|或 & 分别合并选项或交叉组合选项。 若要指示枚举类型声明位字段,请对其应用 Flags 属性。 如下面的示例所示,还可以在枚举类型的定义中包含一些典型组合。
[Flags]
public enum Days
{
None = 0b_0000_0000, // 0
Monday = 0b_0000_0001, // 1
Tuesday = 0b_0000_0010, // 2
Wednesday = 0b_0000_0100, // 4
Thursday = 0b_0000_1000, // 8
Friday = 0b_0001_0000, // 16
Saturday = 0b_0010_0000, // 32
Sunday = 0b_0100_0000, // 64
Weekend = Saturday | Sunday
}
public class FlagsEnumExample
{
public static void Main()
{
Days meetingDays = Days.Monday | Days.Wednesday | Days.Friday;
Console.WriteLine(meetingDays);
// Output:
// Monday, Wednesday, Friday
Days workingFromHomeDays = Days.Thursday | Days.Friday;
Console.WriteLine($"Join a meeting by phone on {meetingDays & workingFromHomeDays}");
// Output:
// Join a meeting by phone on Friday
bool isMeetingOnTuesday = (meetingDays & Days.Tuesday) == Days.Tuesday;
Console.WriteLine($"Is there a meeting on Tuesday: {isMeetingOnTuesday}");
// Output:
// Is there a meeting on Tuesday: False
var a = (Days)37;
Console.WriteLine(a);
// Output:
// Monday, Wednesday, Saturday
}
}
枚举约束
System.Enum 类型是所有枚举类型的抽象基类。 它提供多种方法来获取有关枚举类型及其值的信息。 有关更多信息和示例,请参阅 System.Enum API 参考页。
从 C# 7.3 开始,你可以在基类约束中使用 System.Enum
(称为枚举约束),以指定类型参数为枚举类型。 所有枚举类型也都满足 struct
约束,此约束用于指定类型参数为不可为 null 的值类型。
下面的where是约束了T的范围,范围是T必须是枚举类型
public static Dictionary<int, string> EnumNamedValues<T>() where T : System.Enum
{
var result = new Dictionary<int, string>();
var values = Enum.GetValues(typeof(T));
foreach (int item in values)
result.Add(item, Enum.GetName(typeof(T), item));
return result;
}
enum Rainbow
{
Red,
Orange,
Yellow,
Green,
Blue,
Indigo,
Violet
}
Enum.GetValues
和 Enum.GetName
使用反射,这会对性能产生影响。 可调用 EnumNamedValues
来生成可缓存和重用的集合,而不是重复执行需要反射才能实施的调用。
如以下示例所示,可使用它来创建枚举并生成其值和名称的字典:
var map = EnumNamedValues<Rainbow>();
foreach (var pair in map)
Console.WriteLine($"{pair.Key}:\t{pair.Value}");
与其他数值的显式转换
对于任何枚举类型,枚举类型与其基础整型类型之间存在显式转换。 如果将枚举值转换为其基础类型,则结果为枚举成员的关联整数值。
public enum Season
{
Spring,
Summer,
Autumn,
Winter
}
public class EnumConversionExample
{
public static void Main()
{
Season a = Season.Autumn;
Console.WriteLine($"Integral value of {a} is {(int)a}"); // output: Integral value of Autumn is 2
var b = (Season)1;
Console.WriteLine(b); // output: Summer
var c = (Season)4;
Console.WriteLine(c); // output: 4
}
}
循环与遍历
break和continue都只影响一层循环,continue只会跳出一次循环
for(;;)相当于while(true)
拥有foreach()的应该都实现了IEnumerator接口,拥有GetEnumerator()方法,foreach是迭代器的简记法(简单写法)
迭代器的运用
int[] intArray = new int[] { 1, 2, 3, 4, 5 };
IEnumerator enumerator = intArray.GetEnumerator();
enumerator.MoveNext();//刚开始必须移动 返回布尔值
Console.WriteLine(enumerator.Current);
enumerator.MoveNext();
Console.WriteLine(enumerator.Current);
enumerator.MoveNext();
Console.WriteLine(enumerator.Current);
enumerator.Reset();//归位
enumerator.MoveNext();
Console.WriteLine(enumerator.Current);
输出1 ,2,3,1
内容总结
以上是互联网集市为您收集整理的C#学习日记3 异常处理,为什么要使用异常,枚举类型全部内容,希望文章能够帮你解决C#学习日记3 异常处理,为什么要使用异常,枚举类型所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。