C#: 关键字指南
abstract, as, base, bool, break, byte, case, catch, char, checked, class, const, continue, decimal, default, delegate, do, double, else, enum, event, explicit, extern, false, finally, fixed, float, for, foreach, goto, if, implicit, in, int, interface, internal, is, lock, long, namespace, new, null, object, operator, out, override, params, private, protected, public, readonly, ref, return, sbyte, sealed, short, sizeof, stackalloc, static, string, struct, switch, this, throw, true, try, typeof, uint, ulong, unchecked, unsafe, ushort, using, virtual, void, volatile, while.
abstract
在 C# 中,abstract 关键字用于定义抽象类和抽象方法。抽象类是不能被实例化的类,而抽象方法是没有实现的方法,需要在子类中被实现。
抽象类
抽象类是用于表示一类对象的基类,但是它本身不能被实例化。抽象类通常包含一些抽象方法,这些方法需要在子类中被实现。抽象类可以包含非抽象方法和字段,这些成员可以被子类继承和使用。
定义抽象类的语法如下:
abstract class MyClass { // 抽象方法 public abstract void MyMethod(); // 非抽象方法 public void MyOtherMethod() { // ... } }
抽象方法
抽象方法是没有实现的方法,需要在子类中被实现。抽象方法只有方法签名,没有方法体。定义抽象方法的语法如下:
abstract class MyClass { public abstract void MyMethod(); }
子类必须实现抽象方法,否则会编译错误。例如:
class MyDerivedClass : MyClass { public override void MyMethod() { // 实现抽象方法 } }
总结
as
以下是 as 关键字在 C# 中的使用示例:
object obj = "Hello World"; string str = obj as string;
在上面的代码中,我们有一个包含字符串 "Hello World" 的对象 obj。然后,我们使用 as 关键字将 obj 转换为字符串类型,并将其赋值给变量 str。如果转换无效,则 str 将被赋予空值。
base
访问基类构造函数
在派生类的构造函数中,我们可以使用 base 关键字来调用基类的构造函数。这样做的好处是可以在派生类的构造函数中初始化基类中的字段。
以下是一个例子:
public class MyBaseClass { private int myField; public MyBaseClass(int myField) { this.myField = myField; } } public class MyDerivedClass : MyBaseClass { public MyDerivedClass(int myField) : base(myField) { } }
在上面的例子中,MyDerivedClass 继承自 MyBaseClass。在 MyDerivedClass 的构造函数中,我们使用 base 关键字来调用 MyBaseClass 的构造函数,并将 myField 参数传递给它。
访问基类方法和属性
在派生类中,我们可以使用 base 关键字来调用基类中的方法和属性。以下是一个例子:
public class MyBaseClass { public virtual void MyMethod() { Console.WriteLine("MyBaseClass.MyMethod"); } public virtual int MyProperty { get { return 42; } } } public class MyDerivedClass : MyBaseClass { public override void MyMethod() { base.MyMethod(); Console.WriteLine("MyDerivedClass.MyMethod"); } public override int MyProperty { get { return base.MyProperty + 1; } } }
在上面的例子中,MyDerivedClass 继承自 MyBaseClass。在 MyDerivedClass 中,我们重写了 MyMethod 和 MyProperty 方法,并使用 base 关键字来调用基类中的方法和属性。
bool
在 C# 中,布尔类型是一种基本数据类型,可以用于逻辑运算和条件语句中。例如,可以使用布尔类型来表示某个条件是否成立,或者在循环中判断是否继续执行。
以下是一个简单的示例,演示了如何使用 bool 类型:
bool isTrue = true; bool isFalse = false; if (isTrue) { Console.WriteLine("isTrue is true"); } if (!isFalse) { Console.WriteLine("isFalse is false"); }
在上面的示例中,我们定义了两个布尔变量 isTrue 和 isFalse,并使用 if 语句检查它们的值。第一个 if 语句检查 isTrue 是否为 true,如果是,则输出一条消息。第二个 if 语句使用逻辑非运算符 ! 检查 isFalse 是否为
-
bool isTrue = true;
-
bool isFalse = false;
-
bool isBool = bool.Parse("True");
-
bool isBool = bool.Parse("False");
-
bool isBool = bool.TryParse("True", out bool result);
-
bool isBool = bool.TryParse("False", out bool result);
其中,bool.Parse 方法将字符串转换为布尔值,如果字符串不是有效的布尔值,则会引发异常。bool.TryParse 方法也将字符串转换为布尔值,但如果字符串不是有效的布尔值,则不会引发异常,而是将结果设置为false。
break
以下是一个使用 break 的示例:
while (true) { // do something if (condition) { break; } // do something else }
在上面的代码中,当 condition 满足时,break 语句将被执行,程序将跳出循环并继续执行循环后面的代码。
需要注意的是,break 只能用于循环语句中,不能用于其他语句中。如果在非循环语句中使用 break,编译器将会报错。
byte
以下是一个使用 byte 的示例:
byte b = 255;
在上面的代码中,b 是一个 byte 类型的变量,它存储了一个值为 255 的整数。
需要注意的是,byte 类型的变量只能存储 0 到 255 之间的整数。如果存储的值超出了这个范围,编译器将会报错。
case
以下是一个使用 case 的示例:
switch (variable) { case 1: // do something break; case 2: // do something else break; default: // do something if none of the cases match break; }
在上面的代码中,case 用于匹配 variable 的值。如果 variable 的值等于 1,则执行第一个 case 中的代码;如果 variable 的值等于 2,则执行第二个 case 中的代码;如果 variable 的值不等于 1 或 2,则执行 default 中的代码。
需要注意的是,每个 case 后面必须跟一个 break 语句,否则程序将会继续执行下一个 case 中的代码。如果没有匹配的 case,则执行 default 中的代码。
catch
以下是一个使用 catch 的示例:
try { // do something that may throw an exception } catch (Exception ex) { // handle the exception }
在上面的代码中,try 块中的代码可能会抛出异常。如果发生了异常,程序将跳转到 catch 块中执行相应的代码。catch 块中的参数 ex 是一个 Exception 类型的变量,它用于存储捕获到的异常信息。
需要注意的是,catch 块必须跟在 try 块后面,并且可以有多个 catch 块来处理不同类型的异常。如果没有发生异常,程序将跳过 catch 块并继续执行后面的代码。
char
char c = 'A';
在上面的代码中,c 是一个 char 类型的变量,它存储了一个 Unicode 字符 'A'。
需要注意的是,char 类型的变量只能存储一个字符。如果需要存储多个字符,可以使用字符串类型 string。
另外,char 类型的变量可以使用转义字符来表示一些特殊字符,例如:
char c = '\n'; // 表示换行符
checked
以下是一个使用 checked 的示例:
int a = int.MaxValue; int b = 1; int c = checked(a + b); // 抛出 System.OverflowException 异常
在上面的代码中,a 的值为 int.MaxValue,b 的值为 1。由于 a 和 b 相加的结果超出了 int 类型的取值范围,因此程序将抛出 System.OverflowException 异常。
需要注意的是,checked 关键字只对当前语句块中的算术运算有效。如果需要对整个方法或类中的算术运算进行检查,可以使用 checked 块。
以下是一个使用 checked 块的示例:
checked { int a = int.MaxValue; int b = 1; int c = a + b; // 抛出 System.OverflowException 异常 }
在上面的代码中,checked 块中的所有算术运算都将进行溢出检查。
class
以下是一个使用 class 的示例:
class Person { public string Name { get; set; } public int Age { get; set; } public void SayHello() { Console.WriteLine("Hello, my name is " + Name + " and I am " + Age + " years old."); } }
在上面的代码中,Person 是一个类,它有两个属性 Name 和 Age,以及一个方法 SayHello。属性用于存储数据,方法用于定义行为。
需要注意的是,类必须包含在命名空间中。命名空间用于组织代码,避免命名冲突。
以下是一个包含 Person 类的命名空间的示例:
namespace MyNamespace { class Person { // ... } }
另外,类可以继承自其他类。继承是一种重要的面向对象编程概念,它允许子类继承父类的属性和方法,并可以添加自己的属性和方法。以下是一个继承自 Person 类的 Student 类的示例:
class Student : Person { public string School { get; set; } public void Study() { Console.WriteLine("I am studying at " + School + "."); } }
在上面的代码中,Student 类继承自 Person 类,并添加了一个属性 School 和一个方法 Study。
const
以下是一个使用 const 的示例:
const int MaxValue = 100;
在上面的代码中,MaxValue 是一个常量,它的值为 100。由于 MaxValue 是一个常量,因此它的值在声明后不能被修改。
需要注意的是,常量必须在声明时进行初始化,并且只能使用常量表达式进行初始化。常量表达式是一种在编译时可以确定值的表达式,例如:
const int MaxValue = 100 + 200; // 合法的常量表达式 const int MinValue = GetMinValue(); // 非法的常量表达式
在上面的代码中,MaxValue 的初始化表达式是一个常量表达式,因此它是合法的。而 MinValue 的初始化表达式调用了一个方法 GetMinValue(),因此它不是常量表达式,是非法的。
另外,常量可以用于各种数据类型,包括整型、浮点型、字符型、字符串型等等。以下是一些常量的示例:
const double Pi = 3.1415926; const char NewLine = '\n'; const string Greeting = "Hello, world!";
continue
需要注意的是,continue 关键字只能在循环语句中使用。如果在非循环语句中使用 continue,编译器将会报错。
以下是一个使用 continue 的示例:
int i = 0; while (i < 10) { i++; if (i % 2 == 0) { continue; } Console.WriteLine(i); }
在上面的代码中,我们使用 while 循环从 1 到 10 迭代。然而,我们使用 continue 关键字跳过任何偶数并仅打印奇数。因此,这段代码的输出将是:
你还可以使用标签和 continue 关键字一起跳过嵌套循环中的特定值。以下是一个示例:
for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { if (j == 5) { continue; } Console.WriteLine(i + "," + j); } }
在上面的代码中,我们使用嵌套的 for 循环来迭代从 0 到 9 的所有 i 和 j 的组合。然而,我们使用 continue 来跳过任何值等于 5 的 j。因此,这段代码的输出将是除了 j 等于 5 的所有 i 和 j。
decimal
以下是一些关于decimal的特点和用法:
-
decimal类型的变量可以使用M或m后缀来声明。例如:decimal myDecimal = 123.45M;
-
decimal类型的默认值为0M。
-
decimal类型的运算符和其他数字类型一样,包括+、-、*、/等。
-
decimal类型的运算符可以与其他数字类型进行混合运算,但需要进行类型转换。
-
decimal类型的精度为28-29位小数,而double类型的精度为15-16位小数。
-
decimal类型的范围为±1.0 x 10^-28 到 ±7.9 x 10^28。
以下是一个使用decimal类型的示例代码:
decimal price = 19.99M; decimal taxRate = 0.08M; decimal taxAmount = price * taxRate; decimal total = price + taxAmount; Console.WriteLine("Price: {0:C}", price); Console.WriteLine("Tax: {0:C}", taxAmount); Console.WriteLine("Total: {0:C}", total);
在上面的示例中,我们使用decimal类型来计算商品价格、税率、税额和总价,并使用Console.WriteLine方法将结果输出到控制台。
default
以下是一些关于default的特点和用法:
-
default关键字可以用于任何类型,包括值类型和引用类型。
- 对于值类型default关键字返回该类型的零值。例如,default(int)返回0,default(bool)返回false。对于引用类型,default关键字返回null。
-
default关键字可以用于泛型类型参数,以获取该类型的默认值。例如,default(T)返回T类型的默认值。
-
default关键字可以用于switch语句中的case分支,以表示默认分支。例如:
switch (myVariable) { case 1: // do something break; case 2: // do something else break; default: // do default action break; }
在上面的示例中,如果myVariable的值既不是1也不是2,则执行默认分支。
delegate
-
delegate关键字用于声明委托类型。例如,下面的代码声明了一个名为MyDelegate的委托类型,它可以封装一个返回int类型、参数为string类型的方法:
delegate int MyDelegate(string arg);
-
委托类型可以用于声明委托变量。例如,下面的代码声明了一个名为myDelegate的委托变量,它可以引用一个返回int类型、参数为string类型的方法:
MyDelegate myDelegate = MyMethod;
-
委托变量可以通过+=和-=运算符来添加和移除方法。例如,下面的代码添加了一个名为MyOtherMethod的方法到myDelegate中:
myDelegate += MyOtherMethod;
-
委托变量可以通过()运算符来调用封装的方法。例如,下面的代码调用了myDelegate中封装的方法,并将"hello"作为参数传递给它:
int result = myDelegate("hello");
-
委托类型可以用于声明事件。例如,下面的代码声明了一个名为MyEvent的事件,它使用MyDelegate类型作为事件处理程序的类型:
event MyDelegate MyEvent;
-
事件可以通过+=和-=运算符来添加和移除事件处理程序。例如,下面的代码添加了一个名为MyEventHandler的事件处理程序到MyEvent中:
MyEvent += MyEventHandler;
do
do关键字,用于声明do-while循环。do-while循环是一种先执行循环体,再判断循环条件的循环结构。以下是一些关于do的特点和用法:
do { // do something } while (condition);
-
do-while循环至少会执行一次循环体,即使循环条件一开始就为false。
-
do-while循环可以用于需要至少执行一次的循环场景,例如读取用户输入、处理异常等。
double
以下是一些关于double的特点和用法:
-
double关键字用于声明双精度浮点数类型。例如,下面的代码声明了一个名为myDouble的双精度浮点数变量:
double myDouble = 3.14159;
-
双精度浮点数类型可以表示的范围为±5.0 × 10^-324到±1.7 × 10^308,精度为15-16位小数。
-
双精度浮点数类型可以进行基本的算术运算,例如加减乘除、取余等。例如,下面的代码计算了两个双精度浮点数的和:
double result = myDouble1 + myDouble2;
-
双精度浮点数类型可以进行比较运算,例如等于、大于、小于等。需要注意的是,由于浮点数的精度问题,比较运算可能会出现意外的结果。例如,下面的代码比较了两个双精度浮点数的大小:
bool isGreater = myDouble1 > myDouble2;
else
-
else关键字通常与if关键字一起使用,用于指定当if条件不成立时要执行的代码块。例如,下面的代码使用了if和else关键字,根据condition的值来执行不同的代码块:
if (condition) { // do something if condition is true } else { // do something else if condition is false }
-
else关键字也可以与if关键字的嵌套使用,用于指定多个条件的执行代码块。例如,下面的代码使用了两个if和一个else关键字,根据condition1和condition2的值来执行不同的代码块:
if (condition1) { // do something if condition1 is true } else if (condition2) { // do something else if condition2 is true } else { // do something else if both condition1 and condition2 are false }
-
else关键字可以省略,只使用if关键字来指定条件语句。这种情况下,如果条件不成立,则不执行任何代码。例如,下面的代码只使用了if关键字,如果condition不成立,则不执行任何代码:
if (condition) { // do something if condition is true }
enum
-
enum关键字用于声明枚举类型。例如,下面的代码声明了一个名为Color的枚举类型,它包含了三个常量值Red、Green和Blue:
enum Color { Red, Green, Blue }
-
枚举类型的常量值默认从0开始递增,但是可以手动指定每个常量值的数值。例如,下面的代码声明了一个名为Size的枚举类型,它手动指定了每个常量值的数值:
enum Size { Small = 1, Medium = 2, Large = 3 }
-
枚举类型的常量值可以作为变量使用。例如,下面的代码声明了一个名为myColor的Color类型变量,并将其赋值为Green:
Color myColor = Color.Green;
-
枚举类型的常量值可以用于条件语句中。例如,下面的代码使用了switch语句和Color类型的常量值来执行不同的代码块:
switch (myColor) { case Color.Red: // do something if myColor is Red break; case Color.Green: // do something if myColor is Green break; case Color.Blue: // do something if myColor is Blue break; default: // do something if myColor is not Red, Green or Blue break; }
event
-
event关键字用于声明事件。例如,下面的代码声明了一个名为myEvent的事件:
public event EventHandler myEvent;
-
事件必须与委托类型一起使用。例如,上面的代码中,EventHandler是一个委托类型,它定义了事件处理程序的签名。
-
事件可以在类中声明,也可以在接口中声明。例如,下面的代码声明了一个名为IMyInterface的接口,它包含了一个名为MyEvent的事件:
interface IMyInterface { event EventHandler MyEvent; }
-
事件可以使用+=和-=运算符来添加和移除事件处理程序。例如,下面的代码添加了一个名为myHandler的事件处理程序:
myEvent += myHandler;
-
事件处理程序必须与事件的委托类型具有相同的签名。例如,如果事件的委托类型是EventHandler,则事件处理程序必须具有以下签名:
void MyEventHandler(object sender, EventArgs e)
-
事件处理程序可以是任何方法,只要它们与事件的委托类型具有相同的签名。例如,下面的代码定义了一个名为myHandler的方法,它与EventHandler委托类型具有相同的签名:
void myHandler(object sender, EventArgs e) { // do something when the event is raised }
explicit
-
explicit关键字用于声明显式转换运算符。例如,下面的代码声明了一个名为MyClass的类,并在其中定义了一个名为explicit operator int(MyClass myClass)的显式转换运算符:
class MyClass { public static explicit operator int(MyClass myClass) { // convert myClass to an int } }
-
显式转换运算符必须是公共的和静态的,并且必须返回要转换的类型。例如,上面的代码中,explicit operator int表示将MyClass类型转换为int类型。
-
显式转换运算符可以将一个对象从一种类型转换为另一种类型。例如,下面的代码将一个名为myClass的MyClass类型对象转换为int类型:
MyClass myClass = new MyClass(); int myInt = (int)myClass;
-
显式转换运算符可以用于条件语句中。例如,下面的代码使用了if语句和MyClass类型的对象来执行不同的代码块:
MyClass myClass = new MyClass(); if (myClass == 0) { // do something if myClass is equal to 0 } else { // do something else if myClass is not equal to 0 }
extern
-
extern关键字用于声明外部方法。例如,下面的代码声明了一个名为MessageBox的外部方法:
[DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);
-
外部方法必须使用DllImport属性来指定它们的实现。例如,上面的代码中,user32.dll是包含MessageBox方法实现的动态链接库的名称。
-
外部方法可以在类中声明,也可以在接口中声明。例如,下面的代码声明了一个名为IMyInterface的接口,它包含了一个名为MyMethod的外部方法:
interface IMyInterface { [DllImport("mylib.dll")] void MyMethod(); }
-
外部方法可以使用unsafe关键字来声明不安全的代码块。例如,下面的代码声明了一个名为MyMethod的外部方法,并使用了unsafe关键字:
[DllImport("mylib.dll")] unsafe static extern void MyMethod(byte* buffer, int length);
-
外部方法可以使用ref和out关键字来传递引用参数。例如,下面的代码声明了一个名为MyMethod的外部方法,并使用了ref关键字:
[DllImport("mylib.dll")] static extern void MyMethod(ref int value);
false
-
false关键字用于表示布尔类型的假值。例如,下面的代码将一个名为myBool的布尔类型变量初始化为false:
bool myBool = false;
-
false关键字可以用于条件语句中。例如,下面的代码使用了if语句和布尔类型的变量来执行不同的代码块:
bool myBool = false; if (myBool) { // do something if myBool is true } else { // do something else if myBool is false }
-
false关键字可以与true关键字一起使用,表示布尔类型的真假值。例如,下面的代码使用了if语句和布尔类型的变量来执行不同的代码块:
bool myBool = false; if (myBool == true) { // do something if myBool is true } else { // do something else if myBool is false }
finally
-
finally关键字用于定义一个代码块,该代码块在try块中的代码执行完毕后始终执行。例如,下面的代码使用了try和finally语句来确保文件流被正确关闭:
FileStream fileStream = null; try { fileStream = new FileStream("file.txt", FileMode.Open); // do something with fileStream } finally { if (fileStream != null) { fileStream.Close(); } }
-
finally块中的代码始终会被执行,无论try块中的代码是否抛出异常。例如,下面的代码使用了try和finally语句来确保文件流被正确关闭,即使在try块中的代码抛出异常:
FileStream fileStream = null; try { fileStream = new FileStream("file.txt", FileMode.Open); // do something with fileStream throw new Exception("Something went wrong"); } finally { if (fileStream != null) { fileStream.Close(); } }
-
finally块中的代码可以用于清理资源,例如关闭文件流或释放内存。例如,下面的代码使用了try和finally语句来确保文件流被正确关闭:
FileStream fileStream = null; try { fileStream = new FileStream("file.txt", FileMode.Open); // do something with fileStream } finally { if (fileStream != null) { fileStream.Close(); } }
fixed
-
fixed关键字用于声明一个指针变量,并将其固定在内存中。例如,下面的代码声明了一个名为myArray的整数数组,并使用fixed关键字将其固定在内存中:
int[] myArray = new int[10]; fixed (int* p = myArray) { // do something with p }
-
fixed关键字只能用于指针类型。例如,下面的代码声明了一个名为myPointer的指针变量,并使用fixed关键字将其固定在内存中:
int* myPointer = null; fixed (int* p = &myPointer) { // do something with p }
-
fixed关键字可以用于声明不安全的代码块。例如,下面的代码声明了一个名为MyMethod的方法,并使用了unsafe关键字和fixed关键字:
unsafe static void MyMethod(byte* buffer, int length) { fixed (byte* p = buffer) { // do something with p } }
-
fixed关键字可以用于提高性能。由于固定了指针变量,因此可以避免垃圾回收器移动内存,从而提高代码的性能。但是,使用fixed关键字也可能会导致内存泄漏和安全问题,因此需要谨慎使用。
float
-
float关键字用于表示单精度浮点数类型。例如,下面的代码将一个名为myFloat的单精度浮点数变量初始化为0.0f:
float myFloat = 0.0f;
-
float关键字可以用于表示小数。例如,下面的代码将一个名为myFloat的单精度浮点数变量初始化为3.14f:
float myFloat = 3.14f;
-
float关键字可以用于进行数学运算。例如,下面的代码将两个单精度浮点数相加并将结果存储在一个名为result的变量中:
float num1 = 1.5f; float num2 = 2.5f; float result = num1 + num2;
-
float关键字可以与其他数据类型进行转换。例如,下面的代码将一个名为myInt的整数变量转换为单精度浮点数类型:
int myInt = 10; float myFloat = (float)myInt;
for
for关键字,用于循环执行一段代码。for循环通常用于已知循环次数的情况下,可以在循环中使用计数器来控制循环次数。
以下是一个使用for循环的示例代码:
for (int i = 0; i < 10; i++) { Console.WriteLine(i); }
在上面的代码中,for循环的初始化部分初始化了一个整数变量i,并将其设置为0。条件部分检查i是否小于10,如果是,则执行循环体。循环体中的代码打印i的值。迭代器部分将i的值增加1。
除了上面的示例代码中使用的整数计数器外,for循环还可以使用其他类型的计数器,例如浮点数或日期时间。此外,for循环还可以嵌套在其他循环中,例如while循环或foreach循环中。
foreach
以下是一个使用foreach循环的示例代码:
int[] numbers = { 1, 2, 3, 4, 5 }; foreach (int number in numbers) { Console.WriteLine(number); }
在上面的代码中,foreach循环遍历了一个整数数组numbers中的每个元素,并将其存储在循环变量number中。循环体中的代码打印number的值。
除了上面的示例代码中使用的整数数组外,foreach循环还可以用于遍历其他类型的集合,例如列表、字典等。此外,foreach循环还可以嵌套在其他循环中,例如for循环或while循环中。
goto
以下是一个使用goto语句的示例代码:
int i = 0; start: Console.WriteLine(i); i++; if (i < 10) goto start;
在上面的代码中,goto语句用于跳转到标记为start的位置。循环体中的代码打印i的值,并将其增加1。如果i小于10,则跳转到标记为start的位置。
虽然goto语句可以使程序更加简洁,但它也可能导致代码难以理解和维护。因此,在编写代码时应该尽量避免使用goto语句,而是使用更加结构化的控制流语句,例如if语句、while循环和for循环等。
if
以下是一个使用if语句的示例代码:
int x = 10; if (x > 0) { Console.WriteLine("x是正数"); } else if (x < 0) { Console.WriteLine("x是负数"); } else { Console.WriteLine("x是零"); }
在上面的代码中,if语句用于判断变量x的值。如果x大于0,则打印x是正数;如果x小于0,则打印x是负数;否则打印x是零。
除了上面的示例代码中使用的整数比较外,if语句还可以使用其他类型的比较,例如字符串比较、布尔比较等。
此外,if语句还可以嵌套在其他if语句中,或与其他控制流语句(例如for循环、while循环等)结合使用。
implicit
以下是一个使用implicit关键字定义隐式类型转换的示例代码:
class Celsius { public double Temperature { get; set; } public static implicit operator Fahrenheit(Celsius celsius) { return new Fahrenheit() { Temperature = (celsius.Temperature * 9 / 5) + 32 }; } } class Fahrenheit { public double Temperature { get; set; } } class Program { static void Main(string[] args) { Celsius celsius = new Celsius() { Temperature = 0 }; Fahrenheit fahrenheit = celsius; Console.WriteLine(fahrenheit.Temperature); } }
在上面的代码中,Celsius类定义了一个Temperature属性,表示摄氏温度。Fahrenheit类也定义了一个Temperature属性,表示华氏温度。
Celsius类中定义了一个隐式类型转换运算符,将Celsius类型的值转换为Fahrenheit类型的值。
在Main方法中,创建了一个Celsius对象,并将其赋值给Fahrenheit类型的变量fahrenheit。由于Celsius类定义了隐式类型转换运算符,因此可以将Celsius类型的值隐式转换为Fahrenheit类型的值。
最后,打印fahrenheit.Temperature的值,即0摄氏度对应的华氏温度。
除了上面的示例代码中使用的自定义类型外,implicit关键字还可以用于定义基本类型之间的隐式类型转换,例如将int类型的值隐式转换为long类型的值。
is
class Animal { } class Dog : Animal { } class Program { static void Main(string[] args) { Animal animal = new Dog(); if (animal is Dog) { Console.WriteLine("这是一只狗"); } else { Console.WriteLine("这不是一只狗"); } } }
在上面的代码中,Animal类是一个基类,Dog类是Animal类的派生类。在Main方法中,创建了一个Dog对象,并将其赋值给Animal类型的变量animal。
然后,使用is关键字检查animal对象是否是Dog类型的实例。由于animal对象是Dog类型的实例,因此打印这是一只狗。
除了上面的示例代码中使用的类类型外,is关键字还可以用于检查基本类型的值是否属于指定的范围,例如检查一个整数是否在指定的区间内。
lock
以下是一个使用lock关键字实现互斥锁的示例代码:
class Counter { private int count = 0; private object lockObject = new object(); public void Increment() { lock (lockObject) { count++; } } public void Decrement() { lock (lockObject) { count--; } } public int GetCount() { lock (lockObject) { return count; } } } class Program { static void Main(string[] args) { Counter counter = new Counter(); Task[] tasks = new Task[10]; for (int i = 0; i < 10; i++) { tasks[i] = Task.Run(() => { for (int j = 0; j < 1000; j++) { counter.Increment(); } }); } Task.WaitAll(tasks); Console.WriteLine(counter.GetCount()); } }
在上面的代码中,Counter类表示一个计数器,其中包含Increment、Decrement和GetCount方法。这些方法都使用lock关键字来实现互斥锁,以确保在任何时候只有一个线程可以访问计数器。
在Main方法中,创建了一个Counter对象,并启动了10个任务,每个任务都会调用Increment方法1000次。最后,打印计数器的值,即所有任务调用Increment方法的总次数。
除了上面的示例代码中使用的对象锁外,lock关键字还可以用于锁定其他类型的对象,例如字符串、数组等。
long
以下是一个使用long关键字声明变量的示例代码:
long number = 1234567890123456789L; Console.WriteLine(number);
在上面的代码中,使用long关键字声明了一个名为number的变量,并将其初始化为一个64位整数值。由于整数值超出了int类型的范围,因此需要在数字后面添加一个L后缀,以指示这是一个long类型的值。
然后,使用Console.WriteLine方法打印number变量的值。
除了上面的示例代码中使用的变量类型外,long关键字还可以用于声明long[]类型的数组,以及在方法参数和返回值中使用long类型。
namespace
以下是一个使用namespace关键字定义命名空间的示例代码:
namespace MyNamespace { class MyClass { public void MyMethod() { Console.WriteLine("Hello, world!"); } } } class Program { static void Main(string[] args) { MyNamespace.MyClass myObject = new MyNamespace.MyClass(); myObject.MyMethod(); } }
在上面的代码中,使用namespace关键字定义了一个名为MyNamespace的命名空间,并在其中定义了一个名为MyClass的类。MyClass类包含一个名为MyMethod的方法,该方法打印Hello, world!。
在Main方法中,创建了一个MyClass对象,并调用了MyMethod方法。
除了上面的示例代码中使用的命名空间外,namespace关键字还可以用于定义嵌套命名空间、别名命名空间和全局命名空间。嵌套命名空间是指在一个命名空间中定义另一个命名空间,以便更好地组织代码。
别名命名空间是指为一个命名空间定义一个别名,以便更方便地使用该命名空间中的类型。
全局命名空间是指没有任何命名空间限定符的类型,这些类型可以在任何命名空间中使用。
new
以下是一个使用new关键字创建新对象的示例代码:
class MyBaseClass { public void MyMethod() { Console.WriteLine("MyBaseClass.MyMethod"); } } class MyDerivedClass : MyBaseClass { public new void MyMethod() { Console.WriteLine("MyDerivedClass.MyMethod"); } } class Program { static void Main(string[] args) { MyBaseClass myBaseObject = new MyBaseClass(); myBaseObject.MyMethod(); MyDerivedClass myDerivedObject = new MyDerivedClass(); myDerivedObject.MyMethod(); MyBaseClass myDerivedAsBaseObject = new MyDerivedClass(); myDerivedAsBaseObject.MyMethod(); } }
在上面的代码中,定义了一个名为MyBaseClass的基类和一个名为MyDerivedClass的派生类。在MyDerivedClass中,使用new关键字隐藏了基类中的MyMethod方法,并定义了一个新的MyMethod方法。
在Main方法中,创建了一个MyBaseClass对象和一个MyDerivedClass对象,并分别调用了它们的MyMethod方法。此外,还创建了一个MyDerivedClass对象,并将其转换为MyBaseClass类型,然后调用了它的MyMethod方法。
除了上面的示例代码中使用的对象创建外,new关键字还可以用于隐藏基类中的字段、属性和事件。在隐藏基类成员时,可以使用new关键字来明确指定要隐藏基类成员的名称和签名。
null
以下是一个使用null关键字的示例代码:
string myString = null; if (myString == null) { Console.WriteLine("myString is null"); }
在上面的代码中,使用null关键字将一个名为myString的字符串变量初始化为一个空引用。然后,使用if语句检查myString是否为null,如果是,则打印一条消息。
除了上面的示例代码中使用的变量赋值外,null关键字还可以用于比较引用类型的变量是否为null,以及在方法参数和返回值中使用null表示空引用。
object
在C#中,object类型通常用于以下情况:
- 当您不知道要存储的值的确切类型时。
- 当您需要在不同类型之间进行转换时。
- 当您需要在方法中传递任何类型的参数时。
以下是一些使用object类型的示例:
object obj1 = "Hello World"; // obj1 is of type object object obj2 = 123; // obj2 is of type object object obj3 = new List<int>(); // obj3 is of type object
在上面的示例中,我们可以看到object类型可以存储不同类型的值。
operator
以下是一些使用operator关键字的示例:
public static MyClass operator +(MyClass a, MyClass b) { MyClass result = new MyClass(); result.Value = a.Value + b.Value; return result; }
在上面的示例中,我们定义了一个名为+的运算符,它将两个MyClass对象相加并返回一个新的MyClass对象。我们可以在我们的代码中使用这个运算符,就像使用内置的运算符一样。
out
以下是一个使用out关键字的示例:
public void Divide(int dividend, int divisor, out int quotient, out int remainder) { quotient = dividend / divisor; remainder = dividend % divisor; }
在上面的示例中,我们定义了一个名为Divide的方法,它接受两个整数作为输入,并使用out关键字将两个整数作为输出参数返回。在方法中,我们计算了两个整数的商和余数,并将它们分别赋值给quotient和remainder参数。
在调用Divide方法时,我们需要提供两个整数作为输入参数,并使用out关键字将quotient和remainder参数标记为输出参数。方法执行后,quotient和remainder参数的值将被修改为计算结果。
override
以下是一个使用override关键字的示例:
public class Animal { public virtual void MakeSound() { Console.WriteLine("The animal makes a sound"); } } public class Dog : Animal { public override void MakeSound() { Console.WriteLine("The dog barks"); } }
在上面的示例中,我们定义了一个名为Animal的基类和一个名为Dog的派生类。Animal类中定义了一个虚方法MakeSound,它可以被派生类重写。Dog类中使用override关键字重写了MakeSound方法,并提供了自己的实现。
在调用MakeSound方法时,如果我们使用Animal类的实例调用该方法,则会执行Animal类中的实现。如果我们使用Dog类的实例调用该方法,则会执行Dog类中的实现。
params
以下是一个使用params关键字的示例:
public void PrintNumbers(params int[] numbers) { foreach (int number in numbers) { Console.WriteLine(number); } }
在上面的示例中,我们定义了一个名为PrintNumbers的方法,它接受一个可变数量的整数作为输入,并使用params关键字将它们定义为一个数组。在方法中,我们使用foreach循环遍历数组,并将每个整数打印到控制台上。
在调用PrintNumbers方法时,我们可以向方法传递任意数量的整数作为参数。这些整数将被打包到一个数组中,并传递给PrintNumbers方法。
private
以下是一个使用private关键字的示例:
public class MyClass { private int myPrivateField; private void MyPrivateMethod() { // do something } }
在上面的示例中,我们定义了一个名为MyClass的类,它包含一个私有字段myPrivateField和一个私有方法MyPrivateMethod。这些成员都使用private关键字标记,这意味着只有在MyClass类中才能访问它们。
在其他类中,我们无法直接访问MyClass类中的私有成员。如果我们需要访问这些成员,我们可以提供公共方法或属性来访问它们。
protected
以下是一个使用protected关键字的示例:
public class MyClass { protected int myProtectedField; protected void MyProtectedMethod() { // do something } } public class MyDerivedClass : MyClass { public void MyDerivedMethod() { myProtectedField = 42; MyProtectedMethod(); } }
在上面的示例中,我们定义了一个名为MyClass的类,它包含一个受保护的字段myProtectedField和一个受保护的方法MyProtectedMethod。这些成员都使用protected关键字标记,这意味着只有在MyClass类或其派生类中才能访问它们。
我们还定义了一个名为MyDerivedClass的派生类,它继承自MyClass类。在MyDerivedClass类中,我们可以访问MyClass类中的受保护成员,因为MyDerivedClass类是MyClass类的派生类。
public
以下是一个使用public关键字的示例:
public class MyClass { public int myPublicField; public void MyPublicMethod() { // do something } }
在上面的示例中,我们定义了一个名为MyClass的类,它包含一个公共字段myPublicField和一个公共方法MyPublicMethod。这些成员都使用public关键字标记,这意味着它们可以在任何地方被访问。
在其他类中,我们可以直接访问MyClass类中的公共成员。如果我们需要访问这些成员,我们可以创建一个MyClass类的实例,并使用点号运算符来访问它们。
readonly
以下是一个使用readonly关键字的示例:
public class MyClass { public readonly int myReadOnlyField; public MyClass(int value) { myReadOnlyField = value; } }
在上面的示例中,我们定义了一个名为MyClass的类,它包含一个只读字段myReadOnlyField。这个字段使用readonly关键字标记,这意味着它只能在声明时或在构造函数中进行初始化,并且不能在其他地方进行修改。
在MyClass类的构造函数中,我们将myReadOnlyField字段初始化为传入的value值。一旦初始化完成,这个字段就不能再被修改了。
ref
以下是一个使用ref关键字的示例:
public class MyClass { public void MyMethod(ref int myRefParam) { myRefParam = 42; } } public class Program { static void Main(string[] args) { int myInt = 0; MyClass myClass = new MyClass(); myClass.MyMethod(ref myInt); Console.WriteLine(myInt); // 输出 42 } }
在上面的示例中,我们定义了一个名为MyClass的类,它包含一个名为MyMethod的方法。这个方法使用ref关键字标记其参数myRefParam,这意味着它将作为引用传递而不是值传递。
在MyMethod方法中,我们将myRefParam参数的值设置为42。由于myRefParam是作为引用传递的,因此这个更改将反映回调用方。
在Program类的Main方法中,我们创建了一个名为myInt的整数变量,并将其初始化为0。然后,我们创建了一个MyClass类的实例,并调用其MyMethod方法,将myInt变量作为myRefParam参数传递,并使用ref关键字标记。
最后,我们在控制台上输出myInt变量的值,这将输出42,因为MyMethod方法将其更改为了42。
return
以下是使用return关键字的示例:
public int Add(int x, int y) { return x + y; } int result = Add(1, 2); Console.WriteLine(result); // 输出3
在上面的示例中,Add方法使用return关键字返回两个整数x和y的和。在调用Add方法时,将参数1和2传递给x和y,并将返回值赋给result变量。在Console.WriteLine语句中,将result变量的值输出到控制台。
需要注意的是,使用return关键字返回值时,必须在方法签名中指定返回类型。
例如,以下代码将导致编译错误:
public Add(int x, int y) { return x + y; }
sbyte
以下是使用sbyte关键字的示例:
sbyte a = 100; sbyte b = -50;
在上面的示例中,a和b都是sbyte类型的变量。a的值为100,b的值为-50。 需要注意的是,sbyte类型是一个有符号的整数类型,因此可以表示负数。
另外,由于sbyte类型的取值范围比较小,因此在使用sbyte类型时需要注意数据溢出的问题。
sealed
以下是使用sealed关键字的示例:
public sealed class MyClass { // class implementation } public class MyDerivedClass : MyClass // 编译错误 { // class implementation }
在上面的示例中,MyClass类使用sealed关键字来限制其他类继承它。在定义MyDerivedClass类时,使用MyClass作为基类会导致编译错误,因为MyClass类已经被标记为sealed。
需要注意的是,sealed关键字只能用于类和方法,不能用于其他成员,如字段或属性。另外,使用sealed关键字标记的类或方法不能被继承或重写,但可以被实例化或调用。
short
以下是使用short关键字的示例:
short a = 10000; short b = -20000;
在上面的示例中,a和b都是short类型的变量。a的值为10000,b的值为-20000。
需要注意的是,short类型是一个有符号的整数类型,因此可以表示负数。另外,由于short类型的取值范围比较小,因此在使用short类型时需要注意数据溢出的问题。
sizeof
以下是使用sizeof关键字的示例:
int size = sizeof(int); Console.WriteLine(size); // 输出4
在上面的示例中,sizeof关键字用于获取int类型的大小,并将结果赋值给size变量。在Console.WriteLine语句中,将size变量的值输出到控制台。
需要注意的是,sizeof关键字只能用于值类型,不能用于引用类型。另外,sizeof关键字返回的是指定类型的大小,单位为字节。
stackalloc
以下是使用stackalloc关键字的示例:
int[] array = new int[100]; fixed (int* p = array) { int* q = stackalloc int[10]; for (int i = 0; i < 10; i++) { q[i] = i; } for (int i = 0; i < 10; i++) { Console.WriteLine(q[i]); } }
在上面的示例中,stackalloc关键字用于在堆栈上分配一个包含10个int类型元素的内存块,并将其赋值给q指针。在for循环中,将q指针指向的内存块中的元素赋值为i。在第二个for循环中,将q指针指向的内存块中的元素输出到控制台。
需要注意的是,stackalloc关键字只能用于值类型,不能用于引用类型。另外,使用stackalloc关键字分配的内存块在方法返回后会自动释放,不需要手动释放。
static
以下是使用static关键字的示例:
public class MyClass { public static int Count = 0; public MyClass() { Count++; } } MyClass obj1 = new MyClass(); MyClass obj2 = new MyClass(); Console.WriteLine(MyClass.Count); // 输出2
在上面的示例中,MyClass类中声明了一个静态成员Count,用于记录创建的MyClass对象的数量。在MyClass类的构造函数中,每次创建对象时,都会将Count的值增加1。
在Main方法中,创建了两个MyClass对象,并输出了Count的值,结果为2。
需要注意的是,静态成员是与类相关联的,而不是与类的实例相关联。因此,可以通过类名来访问静态成员,而不需要创建类的实例。另外,静态成员在程序启动时就会被初始化,并且只会被初始化一次。
string
以下是使用string关键字的示例:
string str1 = "Hello"; string str2 = "World"; string str3 = str1 + " " + str2; Console.WriteLine(str3); // 输出"Hello World"
在上面的示例中,string关键字用于声明三个字符串变量str1、str2和str3。在第三行中,使用+运算符将str1、空格和str2连接起来,并将结果赋值给str3。在Console.WriteLine语句中,将str3的值输出到控制台。
需要注意的是,string类型是引用类型,而不是值类型。因此,声明的字符串变量实际上是一个指向字符串对象的引用。
另外,string类型的字符串是不可变的,也就是说,一旦创建了一个字符串对象,就不能修改它的值。如果需要修改字符串的值,可以使用StringBuilder类。
struct
以下是使用struct关键字的示例:
public struct Point { public int X; public int Y; public Point(int x, int y) { X = x; Y = y; } } Point p = new Point(10, 20); Console.WriteLine(p.X); // 输出10
在上面的示例中,struct关键字用于声明一个名为Point的结构体类型,包含两个int类型的字段X和Y。在Point结构体的构造函数中,使用传入的参数对X和Y进行初始化。
在Main方法中,创建了一个Point结构体对象p,并输出了X的值,结果为10。
需要注意的是,结构体是值类型,而不是引用类型。因此,声明的结构体变量实际上是一个包含结构体字段的内存块。另外,结构体变量在创建时会自动初始化为默认值,而不需要手动初始化。
switch
以下是switch语句的基本语法:
switch (expression) { case value1: // code block break; case value2: // code block break; ... default: // code block break; }
expression是要比较的变量或表达式,value1,value2等是可能的值。如果expression的值与某个case的值匹配,则执行相应的代码块。如果没有匹配的case,则执行default代码块(如果存在)。
需要注意的是,每个case代码块必须以break语句结束,否则程序将继续执行下一个case代码块,直到遇到break或default为止。
以下是一个简单的示例,演示了如何使用switch语句:
int day = 4; string dayName; switch (day) { case 1: dayName = "Monday"; break; case 2: dayName = "Tuesday"; break; case 3: dayName = "Wednesday"; break; case 4: dayName = "Thursday"; break; case 5: dayName = "Friday"; break; case 6: dayName = "Saturday"; break; case 7: dayName = "Sunday"; break; default: dayName = "Invalid day"; break; } Console.WriteLine("Today is " + dayName);
在这个例子中,switch语句比较day的值与1到7之间的可能值,并根据匹配的值输出。
this
以下是this关键字的用法示例:
1. 引用实例变量
class MyClass { private int myVar; public MyClass(int myVar) { this.myVar = myVar; } }
在这个例子中,this.myVar引用了类的实例变量myVar,以区分它和构造函数参数myVar。
2. 调用另一个构造函数
class MyClass { private int myVar; public MyClass() : this(0) { } public MyClass(int myVar) { this.myVar = myVar; } }
在这个例子中,第一个构造函数调用了第二个构造函数,使用this关键字来传递参数。
需要注意的是,this关键字只能在实例方法和构造函数中使用,不能在静态方法中使用。
throw
以下是throw关键字的用法示例:
public void DoSomething(int value) { if (value < 0) { throw new ArgumentException("Value cannot be negative", "value"); } // do something }
在这个例子中,如果value小于0,则抛出一个ArgumentException异常,其中包含错误信息和参数名称。
需要注意的是,throw关键字只能在方法中使用,不能在属性或索引器中使用。另外,抛出的异常必须是System.Exception或其派生类的实例。
以下是一个更复杂的示例,演示了如何使用try-catch-finally语句处理异常:
try { // do something } catch (ArgumentException ex) { Console.WriteLine("Argument exception: " + ex.Message); } catch (Exception ex) { Console.WriteLine("Exception: " + ex.Message); } finally { // cleanup code }
在这个例子中,try代码块中的代码可能会抛出异常。如果抛出ArgumentException异常,则执行第一个catch代码块,打印错误信息。
如果抛出其他类型的异常,则执行第二个catch代码块,打印错误信息。无论是否发生异常,都会执行finally代码块中的代码,用于清理资源。
true
以下是true关键字的用法示例:
bool isTrue = true; if (isTrue) { Console.WriteLine("This is true"); }
在这个例子中,isTrue变量被赋值为true,然后在if语句中使用。由于isTrue的值为true,所以if语句中的代码块将被执行,打印"This is true"。
需要注意的是,true关键字只能表示真值,不能表示其他类型的值。另外,true和false是C#中的保留字,不能用作标识符或变量名。
try
以下是try关键字的用法示例:
try { // do something } catch (ArgumentException ex) { Console.WriteLine("Argument exception: " + ex.Message); } catch (Exception ex) { Console.WriteLine("Exception: " + ex.Message); } finally { // cleanup code }
在这个例子中,try代码块中的代码可能会抛出异常。如果抛出ArgumentException异常,则执行第一个catch代码块,打印错误信息。
如果抛出其他类型的异常,则执行第二个catch代码块,打印错误信息。无论是否发生异常,都会执行finally代码块中的代码,用于清理资源。
需要注意的是,try关键字只能在方法中使用,不能在属性或索引器中使用。另外,try语句必须包含至少一个catch块或一个finally块,或者同时包含两者。如果没有catch块或finally块,则编译器会报错。
另外,try-catch-finally语句可以嵌套使用,以处理更复杂的异常情况。
在嵌套的try-catch-finally语句中,内部的try语句必须包含至少一个catch块或一个finally块,或者同时包含两者。如果没有catch块或finally块,则内部的try语句会向外部的try语句传递异常。
typeof
以下是typeof关键字的用法示例:
Type type = typeof(string); Console.WriteLine(type.FullName);
在这个例子中,typeof关键字用于获取string类型的信息,并将其赋值给type变量。然后,FullName属性用于获取类型的完全限定名称,并将其打印到控制台上。
需要注意的是,typeof关键字只能用于获取类型信息,不能用于创建类型的实例。另外,typeof关键字只能用于获取编译时已知的类型信息,不能用于获取运行时动态生成的类型信息。
另外,C#中还有一个GetType方法,可以用于获取对象的类型信息。与typeof关键字不同的是,GetType方法可以用于获取运行时动态生成的类型信息。以下是GetType方法的用法示例:
string str = "Hello, world!"; Type type = str.GetType(); Console.WriteLine(type.FullName);
在这个例子中,GetType方法用于获取str对象的类型信息,并将其赋值给type变量。然后,FullName属性用于获取类型的完全限定名称,并将其打印到控制台上。
uint
以下是uint关键字的用法示例:
uint count = 0; while (count < 10) { Console.WriteLine(count); count++; }
在这个例子中,count变量被赋值为0,然后在while循环中使用。由于count的值小于10,所以while循环中的代码块将被执行,打印count的值并将其递增。当count的值达到10时,循环结束。
需要注意的是,uint关键字只能表示非负整数,不能表示负数。另外,uint和int是不同的类型,不能直接进行赋值或比较。如果需要将uint类型的值转换为int类型的值,可以使用显式类型转换,
例如:
uint u = 10; int i = (int)u;
在这个例子中,u变量被赋值为10,然后使用显式类型转换将其转换为int类型的值,并将其赋值给i变量。
ulong
以下是ulong关键字的用法示例:
ulong count = 0; while (count < 10) { Console.WriteLine(count); count++; }
在这个例子中,count变量被赋值为0,然后在while循环中使用。由于count的值小于10,所以while循环中的代码块将被执行,打印count的值并将其递增。当count的值达到10时,循环结束。
需要注意的是,ulong关键字只能表示非负长整数,不能表示负数。另外,ulong和long是不同的类型,不能直接进行赋值或比较。如果需要将ulong类型的值转换为long类型的值,可以使用显式类型转换,
例如:
ulong u = 10; long l = (long)u;
在这个例子中,u变量被赋值为10,然后使用显式类型转换将其转换为long类型的值,并将其赋值给l变量。
unchecked
以下是一个示例,展示了使用 unchecked 关键字进行整数溢出运算的情况:
int a = int.MaxValue; int b = unchecked(a + 1); Console.WriteLine(b); // 输出 -2147483648
在上面的示例中,a 的值为 int 类型的最大值,即 2147483647。由于 a + 1 会导致整数溢出,因此如果不使用 unchecked 关键字,程序会抛出 System.OverflowException 异常。
但是由于使用了 unchecked 关键字,程序会继续执行,b 的值为 -2147483648。
需要注意的是,使用 unchecked 关键字可能会导致程序出现意外的结果,因此在使用时需要谨慎。如果不确定是否需要使用 unchecked 关键字,建议不要使用,以避免出现意外的结果。
unsafe
以下是一个示例,展示了使用 unsafe 关键字进行指针操作的情况:
unsafe { int[] arr = new int[10]; fixed (int* p = arr) { for (int i = 0; i < 10; i++) { *(p + i) = i; } } }
在上面的示例中,使用 unsafe 关键字声明了一个代码块,其中使用了指针操作。fixed 关键字用于固定数组 arr 的地址,使得指针 p 可以指向数组的首地址。在循环中,使用指针 p 对数组进行了赋值操作。
需要注意的是,使用 unsafe 关键字可能会导致程序出现不安全的操作,因此在使用时需要谨慎。如果不确定是否需要使用 unsafe 关键字,建议不要使用,以避免出现不安全的操作。
ushort
以下是一个示例,展示了使用 ushort 关键字声明变量的情况:
ushort a = 65535; ushort b = 1; ushort c = (ushort)(a + b); Console.WriteLine(c); // 输出 0
在上面的示例中,a 和 b 的值分别为 ushort 类型的最大值和 1。由于 a + b 会导致整数溢出,因此 c 的值为 0。
需要注意的是,使用 ushort 关键字可能会导致程序出现意外的结果,因此在使用时需要谨慎。如果不确定是否需要使用 ushort 关键字,建议根据具体情况进行选择。
using
引入命名空间
在C#中,命名空间用于组织和管理代码,避免命名冲突。使用using
关键字可以在程序中引入命名空间,使得在代码中可以直接使用该命名空间中的类型和成员,而不需要每次都使用完整的命名空间路径。
例如,如果要使用System.Console类输出信息,可以在代码文件的顶部使用using System;语句引入System命名空间,然后在代码中直接使用Console.WriteLine()方法输出信息,而不需要每次都写出完整的命名空间路径。
释放资源
在C#中,一些对象需要手动释放资源,例如文件、数据库连接等。使用using关键字可以自动释放这些对象占用的资源,避免资源泄露和程序崩溃。
例如,如果要读取一个文件的内容,可以使用System.IO.File类的ReadAllText()方法。但是在读取完毕后,需要手动调用Dispose()方法释放文件占用的资源。使用using关键字可以自动释放资源,代码如下:
using System.IO; string path = "path/to/file.txt"; using (StreamReader reader = new StreamReader(path)) { string content = reader.ReadToEnd(); Console.WriteLine(content); }
在上面的代码中,使用using关键字创建了一个StreamReader对象,并在代码块结束时自动调用了Dispose()方法释放资源。
除了引入命名空间和释放资源,using关键字还可以用于其他一些场景。
别名
使用using关键字可以为类型或命名空间创建别名,方便在代码中使用。例如,可以使用以下语句为System.Console类创建别名Console:
using Console = System.Console;
然后就可以在代码中直接使用Console.WriteLine()方法输出信息,而不需要写出完整的命名空间路径。
静态类
在C# 6.0及以上版本中,可以使用using static关键字引入静态类的成员,使得在代码中可以直接使用该静态类的成员,而不需要使用类名限定符。
例如,可以使用以下语句引入System.Math静态类的成员:
using static System.Math;
然后就可以在代码中直接使用Abs()、Sin()等方法,而不需要使用Math类名限定符。
virtual
虚方法
在C#中,方法可以被重写,即在子类中重新定义一个与父类中同名、同参数列表的方法。但是,如果在父类中定义的方法是非虚方法,那么在子类中重写该方法时,只能使用new关键字隐藏父类中的方法,而不能真正地重写该方法。
如果在父类中定义的方法是虚方法,那么在子类中重写该方法时,可以使用override关键字真正地重写该方法。同时,子类中也可以使用base关键字调用父类中的虚方法。
例如,可以在父类中定义一个虚方法GetInfo(),然后在子类中重写该方法:
class Animal { public virtual void GetInfo() { Console.WriteLine("This is an animal."); } } class Dog : Animal { public override void GetInfo() { base.GetInfo(); Console.WriteLine("This is a dog."); } }
在上面的代码中,Animal类中定义了一个虚方法GetInfo(),然后Dog类中重写了该方法,并使用base关键字调用了父类中的虚方法。当调用Dog类的GetInfo()方法时,会先输出"This is an animal.",然后再输出"This is a dog."。
虚属性
除了虚方法,C#中还有虚属性。虚属性是一种可以被重写的属性。使用virtual关键字可以定义虚属性。
例如,可以在父类中定义一个虚属性Name,然后在子类中重写该属性:
class Animal { public virtual string Name { get; set; } } class Dog : Animal { private string _name; public override string Name { get { return _name; } set { _name = value + " (dog)"; } } }
在上面的代码中,Animal类中定义了一个虚属性Name,然后Dog类中重写了该属性,并在set访问器中添加了一些逻辑。当设置Dog类的Name属性时,会在原有的值后面添加"(dog)"。
虚索引器
除了虚属性,C#中还有虚索引器。虚索引器是一种可以被重写的索引器。使用virtual关键字可以定义虚索引器。
例如,可以在父类中定义一个虚索引器this[int index],然后在子类中重写该索引器:
class Animal { public virtual string this[int index] { get { return "Animal " + index; } } } class Dog : Animal { public override string this[int index] { get { return "Dog " + index; } } }
在上面的代码中,Animal类中定义了一个虚索引器this[int index],然后Dog类中重写了该索引器。当使用Dog类的索引器时,会返回"Dog "加上索引值。
void
在C#中,方法可以有返回值,也可以没有返回值。如果一个方法没有返回值,就可以使用void关键字来表示。例如,下面的代码定义了一个没有返回值的方法PrintHello():
public void PrintHello() { Console.WriteLine("Hello!"); }
在上面的代码中,PrintHello()方法没有返回值,因此使用了void关键字来表示。
需要注意的是,如果一个方法有返回值,就不能使用void关键字来表示。例如,下面的代码定义了一个有返回值的方法Add():
public int Add(int a, int b) { return a + b; }
在上面的代码中,Add()方法有返回值,因此没有使用void关键字来表示。
volatile
在多线程编程中,如果一个字段被多个线程同时访问和修改,就可能会出现数据不一致的问题。这是因为多个线程可能会同时读取和修改同一个字段,导致数据出现错误。
为了解决这个问题,可以使用volatile关键字来表示一个字段是易变的。这样,编译器就会生成特殊的代码来保证多个线程访问和修改该字段时的正确性。
例如,下面的代码定义了一个使用volatile关键字的字段counter:
class Counter { private volatile int counter = 0; public void Increment() { counter++; } public int GetCounter() { return counter; } }
在上面的代码中,counter字段被定义为volatile,表示它是易变的。当多个线程同时调用Increment()方法时,会对counter字段进行自增操作。由于counter字段是易变的,因此编译器会生成特殊的代码来保证多个线程访问和修改该字段时的正确性。
需要注意的是,volatile关键字只能保证字段的可见性和有序性,不能保证原子性。如果需要保证原子性,可以使用lock关键字或Interlocked类等其他方式来实现。
while
例如,下面的代码定义了一个使用while关键字的循环:
int i = 0; while (i < 10) { Console.WriteLine(i); i++; }
在上面的代码中,while关键字后面的条件是i < 10,表示只要i的值小于10,就会重复执行循环中的代码块。循环中的代码块会输出i的值,并将i的值加1,直到i的值不再小于10为止。
需要注意的是,如果循环条件一直为真,就会导致无限循环。因此,在使用while循环时,需要确保循环条件最终会变为假,否则程序会一直执行下去。