C#基础语法与数据类型详解
C# 基础语法
一、命名规则
-
标识符命名 在 C# 中,标识符是用来给变量、函数、类、命名空间等元素命名的。其命名规则如下:
- 必须以字母、下划线(_)或 @ 开头。例如:
myVariable
、_privateVar
、@temp
都是合法的标识符开头。不过,以 @ 开头的标识符主要用于与 C# 关键字冲突的情况,实际编程中较少使用。 - 后续字符可以是字母、数字、下划线。例如:
myVar123
是合法的,而my - var
是不合法的,因为其中包含了连字符。 - 不能与 C# 关键字同名,除非使用 @ 前缀。例如,不能直接命名变量为
class
,但可以命名为@class
。不过,为了代码的可读性,尽量避免使用这种方式。
- 必须以字母、下划线(_)或 @ 开头。例如:
-
命名规范 虽然命名规则定义了哪些标识符是合法的,但命名规范则有助于提高代码的可读性和可维护性。常见的命名规范有:
- Pascal 命名法:每个单词的首字母大写,用于类、方法、属性等命名。例如:
MyClass
、CalculateTotal
、UserFullName
。 - Camel 命名法:第一个单词的首字母小写,其余单词首字母大写,常用于变量命名。例如:
myVariable
、userAge
。 - 常量命名:通常使用全大写字母,单词间用下划线分隔。例如:
MAX_VALUE
、PI
。
- Pascal 命名法:每个单词的首字母大写,用于类、方法、属性等命名。例如:
二、语句和块
- 语句 C# 中的语句是执行程序操作的基本单位。例如,声明变量并赋值的语句:
int number = 10;
这条语句声明了一个整型变量 number
并将其初始化为 10。语句以分号(;)结束,分号表示语句的终结。
- 块 块是由一对花括号({})括起来的零条或多条语句。例如:
if (number > 5)
{
Console.WriteLine("The number is greater than 5.");
int newNumber = number * 2;
Console.WriteLine("The new number is: " + newNumber);
}
在这个例子中,if
条件满足时执行的代码部分就是一个块。块可以包含多条语句,并且可以嵌套在其他块中,比如在 if
、while
、for
等控制结构中。
三、注释
注释是在代码中添加的解释性文本,编译器会忽略注释内容。C# 支持三种类型的注释:
- 单行注释:以双斜杠(//)开头,到本行末尾结束。例如:
int age = 25; // 这是一个表示年龄的变量
- 多行注释:以 /* 开始,以 */ 结束,可以跨越多行。例如:
/*
这是一段多行注释,
可以用于对复杂代码块进行详细说明。
这里的代码是计算两个数的和。
*/
int sum = num1 + num2;
- 文档注释:以 /// 开头,主要用于为代码生成 XML 文档。例如:
/// <summary>
/// 计算两个整数的和
/// </summary>
/// <param name="a">第一个整数</param>
/// <param name="b">第二个整数</param>
/// <returns>两个整数的和</returns>
public static int Add(int a, int b)
{
return a + b;
}
文档注释中的标签(如 <summary>
、<param>
、<returns>
)可以被工具解析,生成详细的代码文档,方便其他开发人员理解代码的功能和使用方法。
四、控制结构
- if - else 语句
if - else
语句用于根据条件执行不同的代码块。基本语法如下:
if (condition)
{
// 条件为真时执行的代码
}
else
{
// 条件为假时执行的代码
}
例如:
int score = 85;
if (score >= 60)
{
Console.WriteLine("Passed");
}
else
{
Console.WriteLine("Failed");
}
可以有多个 else if
子句来处理多个条件:
if (score >= 90)
{
Console.WriteLine("A");
}
else if (score >= 80)
{
Console.WriteLine("B");
}
else if (score >= 70)
{
Console.WriteLine("C");
}
else if (score >= 60)
{
Console.WriteLine("D");
}
else
{
Console.WriteLine("F");
}
- switch - case 语句
switch - case
语句用于基于一个表达式的值来执行不同的代码块。语法如下:
switch (expression)
{
case value1:
// 当 expression 等于 value1 时执行的代码
break;
case value2:
// 当 expression 等于 value2 时执行的代码
break;
default:
// 当 expression 不等于任何 case 值时执行的代码
break;
}
例如,根据一周中的某一天输出对应的信息:
int day = 3;
switch (day)
{
case 1:
Console.WriteLine("Monday");
break;
case 2:
Console.WriteLine("Tuesday");
break;
case 3:
Console.WriteLine("Wednesday");
break;
case 4:
Console.WriteLine("Thursday");
break;
case 5:
Console.WriteLine("Friday");
break;
case 6:
Console.WriteLine("Saturday");
break;
case 7:
Console.WriteLine("Sunday");
break;
default:
Console.WriteLine("Invalid day");
break;
}
在每个 case
块中,break
语句用于跳出 switch
语句。如果没有 break
,代码会继续执行下一个 case
块,直到遇到 break
或 switch
结束。
- 循环结构
- for 循环:用于执行已知次数的循环。语法为:
for (initialization; condition; iteration)
{
// 循环体
}
例如,打印 1 到 10 的数字:
for (int i = 1; i <= 10; i++)
{
Console.WriteLine(i);
}
在这个例子中,int i = 1
是初始化部分,i <= 10
是条件部分,i++
是迭代部分。每次循环开始时,先检查条件是否满足,满足则执行循环体,然后执行迭代部分,再重新检查条件。
- while 循环:用于在条件为真时重复执行代码块。语法为:
while (condition)
{
// 循环体
}
例如,计算 1 到 100 的累加和:
int sum = 0;
int num = 1;
while (num <= 100)
{
sum += num;
num++;
}
Console.WriteLine("Sum: " + sum);
这里先检查 num <= 100
的条件,满足则执行循环体,在循环体中更新 num
的值,然后再次检查条件。
- do - while 循环:与 while
循环类似,但它先执行一次循环体,然后再检查条件。语法为:
do
{
// 循环体
} while (condition);
例如:
int input;
do
{
Console.WriteLine("Enter a number between 1 and 10 (0 to quit): ");
input = int.Parse(Console.ReadLine());
if (input >= 1 && input <= 10)
{
Console.WriteLine("You entered: " + input);
}
} while (input!= 0);
在这个例子中,无论条件是否满足,都会先执行一次循环体,然后根据输入的值决定是否继续循环。
C# 数据类型
一、值类型
- 整数类型
C# 提供了多种整数类型,用于表示不同范围的整数值。
- sbyte:8 位有符号整数,范围是 -128 到 127。例如:
sbyte smallNumber = -100;
- **byte**:8 位无符号整数,范围是 0 到 255。例如:
byte positiveNumber = 200;
- **short**:16 位有符号整数,范围是 -32,768 到 32,767。例如:
short mediumNumber = -10000;
- **ushort**:16 位无符号整数,范围是 0 到 65,535。例如:
ushort unsignedMediumNumber = 30000;
- **int**:32 位有符号整数,范围是 -2,147,483,648 到 2,147,483,647。这是最常用的整数类型,例如:
int largeNumber = 1000000;
- **uint**:32 位无符号整数,范围是 0 到 4,294,967,295。例如:
uint unsignedLargeNumber = 2000000000;
- **long**:64 位有符号整数,范围是 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。例如:
long veryLargeNumber = 1000000000000;
- **ulong**:64 位无符号整数,范围是 0 到 18,446,744,073,709,551,615。例如:
ulong unsignedVeryLargeNumber = 1000000000000000000;
- 浮点类型
- float:32 位单精度浮点数,用于表示小数。它的精度大约是 7 位有效数字。例如:
float piApprox = 3.14159f;
注意,在赋值时需要在数字后面加上 f
或 F
,以表明这是一个 float
类型。
- double:64 位双精度浮点数,精度大约是 15 - 17 位有效数字。这是更常用的表示小数的类型,例如:
double eApprox = 2.71828182845904523536;
- decimal 类型
decimal
类型用于表示高精度的小数,适合用于财务和货币计算。它具有 28 - 29 位有效数字。例如:
decimal price = 19.99m;
同样,在赋值时需要在数字后面加上 m
或 M
,以表明这是一个 decimal
类型。与 float
和 double
相比,decimal
类型在表示精确小数方面更有优势,因为 float
和 double
在某些小数的表示上可能存在精度损失。例如,计算 0.1 + 0.2 时,float
和 double
可能无法得到精确的 0.3,而 decimal
可以:
float floatSum = 0.1f + 0.2f;
double doubleSum = 0.1 + 0.2;
decimal decimalSum = 0.1m + 0.2m;
Console.WriteLine("Float sum: " + floatSum);
Console.WriteLine("Double sum: " + doubleSum);
Console.WriteLine("Decimal sum: " + decimalSum);
- 字符类型
char
类型用于表示单个字符,它是 16 位无符号整数,对应于 Unicode 字符集。例如:
char letter = 'A';
可以使用转义序列来表示特殊字符,例如 \n
表示换行,\t
表示制表符,\\
表示反斜杠等。例如:
Console.WriteLine("This is a line\nThis is a new line");
- 布尔类型
bool
类型只有两个值:true
和false
,用于表示逻辑值。例如:
bool isDone = true;
if (isDone)
{
Console.WriteLine("The task is completed.");
}
二、引用类型
- 字符串类型
string
类型用于表示字符串,即一系列字符。字符串是不可变的,这意味着一旦创建了一个字符串对象,就不能修改其内容。例如:
string greeting = "Hello, World!";
可以使用 +
运算符来连接字符串:
string part1 = "Hello";
string part2 = " World";
string fullGreeting = part1 + part2;
也可以使用 string.Format
方法来格式化字符串:
int age = 30;
string message = string.Format("The person is {0} years old.", age);
在 C# 6.0 及更高版本中,还可以使用字符串插值:
string interpolatedMessage = $"The person is {age} years old.";
- 类类型 类是 C# 中最重要的引用类型之一,它封装了数据和行为。定义一个类的基本语法如下:
public class Person
{
// 字段
private string name;
private int age;
// 属性
public string Name
{
get { return name; }
set { name = value; }
}
public int Age
{
get { return age; }
set { age = value; }
}
// 构造函数
public Person(string initialName, int initialAge)
{
name = initialName;
age = initialAge;
}
// 方法
public void Introduce()
{
Console.WriteLine($"Hello, I'm {name} and I'm {age} years old.");
}
}
可以通过以下方式使用这个类:
Person person1 = new Person("Alice", 25);
person1.Introduce();
- 数组类型 数组是一种用于存储多个相同类型元素的数据结构。声明和初始化数组的方式有多种。例如,声明一个整型数组并初始化:
int[] numbers = new int[5] { 1, 2, 3, 4, 5 };
也可以省略数组大小,直接根据初始化的值来确定:
int[] otherNumbers = { 10, 20, 30 };
可以通过索引访问数组元素,索引从 0 开始。例如:
int firstNumber = numbers[0];
还可以创建多维数组,例如二维数组:
int[,] matrix = new int[2, 3] { { 1, 2, 3 }, { 4, 5, 6 } };
int value = matrix[1, 2]; // 获取第二行第三列的元素
- 接口类型 接口定义了一组方法、属性、事件等的签名,但不包含实现。类可以实现接口,以表明它提供了接口定义的功能。定义接口的语法如下:
public interface IPrintable
{
void Print();
}
然后一个类可以实现这个接口:
public class Document : IPrintable
{
public void Print()
{
Console.WriteLine("Printing document...");
}
}
这样,任何 Document
对象都可以被当作 IPrintable
对象使用,例如:
IPrintable printable = new Document();
printable.Print();
三、类型转换
- 隐式类型转换
隐式类型转换是自动进行的,当从一个较小范围的类型转换到一个较大范围的类型时,通常会发生隐式类型转换。例如,从
int
到long
:
int num = 100;
long bigNum = num;
因为 long
可以表示比 int
更大的范围,所以这种转换是安全的,编译器会自动进行。同样,从 int
到 float
、double
等也会发生隐式转换:
float floatNum = num;
double doubleNum = num;
- 显式类型转换
当从一个较大范围的类型转换到一个较小范围的类型时,或者在不兼容的类型之间转换时,需要进行显式类型转换,也称为强制类型转换。例如,从
double
到int
:
double doubleValue = 10.5;
int intValue = (int)doubleValue; // 结果为 10,小数部分被截断
对于不兼容的类型,如 string
到 int
,需要使用特定的转换方法。例如:
string numberString = "123";
int parsedNumber = int.Parse(numberString);
但如果字符串内容无法正确解析为 int
,会抛出异常。为了避免异常,可以使用 int.TryParse
方法:
string badString = "abc";
int result;
bool success = int.TryParse(badString, out result);
if (success)
{
Console.WriteLine("Parsed successfully: " + result);
}
else
{
Console.WriteLine("Failed to parse.");
}
- 装箱和拆箱
装箱是将值类型转换为引用类型,拆箱则是将引用类型转换回值类型。例如,将
int
装箱为object
:
int value = 42;
object boxedValue = value; // 装箱
然后可以将其拆箱回 int
:
int unboxedValue = (int)boxedValue; // 拆箱
需要注意的是,在拆箱时,如果引用对象的实际类型与要拆箱的类型不匹配,会抛出 InvalidCastException
异常。装箱和拆箱操作会带来一定的性能开销,因为涉及到堆内存的分配和类型检查,所以在性能敏感的代码中应尽量避免不必要的装箱和拆箱。
四、自定义数据类型
- 结构体 结构体是一种值类型,它可以包含不同类型的成员。定义结构体的语法与类类似,但结构体是值类型,而类是引用类型。例如:
public struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
public void Move(int dx, int dy)
{
X += dx;
Y += dy;
}
}
可以这样使用结构体:
Point point1 = new Point(10, 20);
point1.Move(5, 5);
Console.WriteLine($"X: {point1.X}, Y: {point1.Y}");
结构体在创建时直接在栈上分配内存,而类对象在堆上分配内存。结构体适合用于表示轻量级的数据结构,如几何点、尺寸等。 2. 枚举 枚举是一种特殊的值类型,用于定义一组命名的常量。例如,定义一个表示一周中各天的枚举:
public enum DayOfWeek
{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
可以使用枚举类型的变量:
DayOfWeek today = DayOfWeek.Wednesday;
if (today == DayOfWeek.Saturday || today == DayOfWeek.Sunday)
{
Console.WriteLine("It's the weekend!");
}
else
{
Console.WriteLine("It's a weekday.");
}
枚举常量默认从 0 开始赋值,可以显式指定值。例如:
public enum StatusCode
{
Success = 200,
NotFound = 404,
ServerError = 500
}
这样,StatusCode.Success
的值就是 200,StatusCode.NotFound
的值就是 404 等。枚举在代码中用于提高可读性和可维护性,避免使用魔法数字。
- 委托 委托是一种引用类型,它可以存储对方法的引用。委托允许将方法作为参数传递给其他方法,实现回调机制。定义委托的语法如下:
public delegate void MyDelegate();
然后可以定义一个方法与该委托匹配:
public void MyMethod()
{
Console.WriteLine("This is my method.");
}
可以创建委托实例并调用它:
MyDelegate myDelegate = new MyDelegate(MyMethod);
myDelegate();
也可以使用匿名方法或 lambda 表达式来初始化委托:
MyDelegate anonymousDelegate = delegate () { Console.WriteLine("This is an anonymous method."); };
anonymousDelegate();
MyDelegate lambdaDelegate = () => Console.WriteLine("This is a lambda expression.");
lambdaDelegate();
委托在事件处理、异步编程等方面有广泛应用。例如,在图形用户界面编程中,按钮的点击事件可以通过委托来处理。
- 事件
事件是基于委托的一种机制,用于在对象状态改变或特定操作发生时通知其他对象。定义事件通常分为以下几步:
- 定义委托类型:
public delegate void MyEventHandler(object sender, EventArgs e);
这里,object sender
表示引发事件的对象,EventArgs e
包含事件相关的数据。EventArgs
是一个基类,可根据需要创建自定义的事件参数类继承自它。
- 在类中定义事件:
public class MyClass
{
public event MyEventHandler MyEvent;
public void DoSomething()
{
// 执行一些操作
if (MyEvent!= null)
{
MyEvent(this, EventArgs.Empty);
}
}
}
- 订阅和处理事件:
public class Program
{
public static void Main()
{
MyClass myObject = new MyClass();
myObject.MyEvent += MyEventHandlerMethod;
myObject.DoSomething();
}
public static void MyEventHandlerMethod(object sender, EventArgs e)
{
Console.WriteLine("The event was raised.");
}
}
在这个例子中,当 myObject.DoSomething()
方法被调用时,如果有对象订阅了 MyEvent
事件,就会执行订阅的事件处理方法 MyEventHandlerMethod
。事件是实现观察者模式的重要手段,它使得对象之间的交互更加灵活和可维护。
通过深入了解 C# 的基础语法和数据类型,开发人员可以编写出高效、可读且易于维护的代码,为构建各种类型的应用程序奠定坚实的基础。无论是桌面应用、Web 应用还是移动应用,对这些基础知识的掌握都是至关重要的。