C#基础篇(01)一篇文章搞定C#基础语法

1. 基本数据类型

在 C# 中,​基本值类型​(如 intfloatchar 等)的字节数是固定的,与操作系统位数(32位或64位)无关,因为它们是由 ​.NET 规范​ 定义的。

但是,string(字符串)是引用类型,内存占用是不固定的,取决于字符串的长度和编码方式。string在内存中的大小由以下部分组成:

  • 基础开销​(固定部分):
    • 对象头​(8 字节,32位系统;16 字节,64位系统)
    • 类型指针​(4 字节,32位;8 字节,64位)
    • 字符串长度字段​(4 字节)
  • 实际字符数据​(可变部分):
    • 每个 char 占用 ​2 字节​(UTF-16 编码)
    • 字符串末尾有一个 ​空终止符​(\0,2 字节)
  • 例如:"Hello"(5个字符)
    • 32位系统:8 + 4 + 4 + (5×2) + 2 = 28 字节
    • 64位系统:16 + 8 + 4 + (5×2) + 2 = 40 字节

(1)整数类型

类型 字节数 位数 范围
sbyte 1 8 -128 到 127
byte 1 8 0 到 255
short 2 16 -32,768 到 32,767
ushort 2 16 0 到 65,535
int 4 32 -2,147,483,648 到 2,147,483,647
uint 4 32 0 到 4,294,967,295
long 8 64 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
ulong 8 64 0 到 18,446,744,073,709,551,615

(2)浮点类型

类型 字节数 位数 范围
float 4 32 ±1.5×10⁻⁴⁵ 到 ±3.4×10³⁸,7位精度
double 8 64 ±5.0×10⁻³²⁴ 到 ±1.7×10³⁰⁸,15-16位精度
decimal 16 128 ±1.0×10⁻²⁸ 到 ±7.9×10²⁸,28-29位精度

(3)其他类型

类型 字节数 位数 范围
bool 1 8 true/false
char 2 16
string

2. 型的强制转换

(1)隐式转换是编译器自动执行的类型转换,不需要特殊语法,且不会丢失数据。隐式转换通常需要满足从较小整数类型到较大整数类型(如 int 到 long):

int i = 123;
long l = i; // 隐式转换为long
float f = l; // 隐式转换为float

(2)显式转换需要使用强制转换运算符,可能丢失数据或引发异常:

double d = 123.456;
int i = (int)d; // 显式转换为int,小数部分被截断

(3)使用 Convert 类,System.Convert 类提供了一系列静态方法用于类型转换:

string str = "123";
int i = Convert.ToInt32(str);
double d = Convert.ToDouble("123.45");

(4)Parse 和 TryParse 方法,基本类型都提供了 Parse 和 TryParse 方法:

// Parse 方法 - 转换失败会抛出异常
int i = int.Parse("123");

// TryParse 方法 - 转换失败不会抛出异常
bool success = int.TryParse("123", out int result);

3. 常用的转义字符

转义字符是C#中用于表示特殊字符序列的字符组合,以反斜杠(\)开头。它们允许你在字符串中插入无法直接输入或具有特殊含义的字符:

\' 单引号 \0  空字符(null) \t 水平制表符
\" 双引号 \n 换行 \v 垂直制表符
\\  反斜杠 \r 回车 \f

换页

string path = "C:\\Users\\John\\Documents";  // 使用转义字符表示路径
string quote = "He said, \"Hello World!\"";  // 在字符串中包含双引号
string newLine = "First line\nSecond line";  // 换行

当字符串中包含大量反斜杠或转义字符时,可以使用@前缀创建逐字字符串字面量,这样反斜杠就不会被解释为转义字符,这样可以更简洁的输出 "\" 等其他转义字符 :

string path = @"C:\Users\John\Documents";  // 不需要转义反斜杠

string multiLine = @"第一行
第二行
第三行";  // 直接包含换行

string quote = @"He said, ""Hello World!""";    // 在逐字字符串中,要表示双引号需要使用两个双引号:

4. 格式化输出

(1)string.Format方法允许使用占位符 {0}{1}{2}... 来插入变量,并可以指定格式。

string name = "Alice";
int age = 25;
double salary = 1234.567;

string formatted = string.Format("Name: {0}, Age: {1}, Salary: {2}", name, age, salary);
Console.WriteLine(formatted);

// 可以在占位符 {n} 后面加 :格式 来控制输出样式
double salary = 1234.567;
string formatted = string.Format("Salary: {0:C2}", salary); // C2 = 货币格式,2位小数
Console.WriteLine(formatted);

常用格式说明符:

格式 说明 示例
{0:C} 货币格式 1234.567 → $1,234.57
{0:N2} 数字格式(2位小数) 1234.567 → 1,234.57
{0:P} 百分比格式 0.123 → 12.30%
{0:D4} 整数补零(仅限整数) 25 → 0025
{0:yyyy-MM-dd} 日期格式 DateTime.Now → 2024-06-20

(2)插值字符串($""是一种更简洁的格式化方式,直接在字符串中嵌入变量和表达式。

string name = "Bob";
int age = 30;
Console.WriteLine($"Name: {name}, Age: {age}");

// 可以在变量后加 :格式
double salary = 1234.567;
Console.WriteLine($"Salary: {salary:C2}"); // 货币格式,2位小数

(3)Console.WriteLine可以直接使用 string.Format 风格的格式化。

string name = "Charlie";
int score = 95;
Console.WriteLine("Player: {0}, Score: {1:D3}", name, score); // D3 = 3位数字,补零

(4)ToString()方法,所有类型都有 ToString() 方法,可以传入格式字符串。

DateTime now = DateTime.Now;
Console.WriteLine(now.ToString("yyyy-MM-dd HH:mm:ss")); // 自定义日期格式

5. 条件按判断语句

(1)if-else if-else 语句

if (condition1)
{
    // 条件1为 true 时执行的代码
}
else if (condition2)
{
    // 条件2为 true 时执行的代码
}
else
{
    // 所有条件都为 false 时执行的代码
}

(2)switch语句

switch (expression)
{
    case value1:
        // 代码块
        break;
    case value2:
        // 代码块
        break;
    case value3:
    case value4:
        // 代码块
        break;
    default:
        // 默认代码块
        break;
}

(3)三元条件运算符

简洁的条件表达式

int number = 10;
string result = number > 0 ? "正数" : "非正数";
Console.WriteLine(result); // 输出: 正数

6. 循环语句

(1)for循环

明确知道循环次数时使用,包含初始化、条件检查和迭代部分

for (int i = 0; i < 5; i++)
{
    Console.WriteLine($"当前值: {i}");
}

(2)foreach循环

用于遍历集合或数组,不需要维护计数器,不能修改集合中的元素(只读访问)

string[] colors = { "红", "绿", "蓝" };
foreach (string color in colors)
{
    Console.WriteLine(color);
}

(3)while循环

条件为真时执行循环,循环次数不确定时使用,可能一次都不执行(条件初始为假)

int count = 3;
while (count > 0)
{
    Console.WriteLine($"剩余次数: {count}");
    count--;
}

(4)do-while循环

至少执行一次循环体,执行后检查条件,适用于需要至少执行一次的场景

int number;
do
{
    Console.Write("请输入一个正数: ");
} while (!int.TryParse(Console.ReadLine(), out number) || number <= 0);

break:立即终止整个循环,并跳出循环体,继续执行循环之后的代码。

continue:跳过当前迭代的剩余代码,直接进入循环的下一次迭代(不终止整个循环)。

7. 数组

(1)声明&初始化数组

// 声明一个整数数组(未初始化)
int[] numbers;
// 声明一个字符串数组
string[] names;

// 方式1:声明时指定大小
int[] numbers1 = new int[5]; // 5个元素的数组,默认值为0
// 方式2:声明时初始化元素
int[] numbers2 = new int[] { 1, 2, 3, 4, 5 };
// 方式3:简化语法
int[] numbers3 = { 1, 2, 3, 4, 5 };
// 方式4:先声明后初始化
int[] numbers4;
numbers4 = new int[] { 1, 2, 3, 4, 5 };

(2)访问数组元素

int[] numbers = { 10, 20, 30, 40, 50 };
// 访问第一个元素
int first = numbers[0]; // 10
// 修改第三个元素
numbers[2] = 35; // 现在数组变为 {10, 20, 35, 40, 50}
// 获取数组长度
int length = numbers.Length; // 5

// 遍历数组方式1:
for (int i = 0; i < numbers.Length; i++)
{
    Console.WriteLine(numbers[i]);
}
// 遍历数组方式2:
foreach (int i in numbers)
{
    Console.WriteLine(i);
}

(3)字符串可以看作char数组

string str1 = "Hello";
string str2 = "HelloWorld";

int length = str1.Length; // 获取长度 → 5
bool isEmpty = string.IsNullOrEmpty(str1); // 检查是否为空或null

int result1 = string.Compare(str1, str2); // 区分大小写比较
int result2 = string.Compare(str1, str2, StringComparison.OrdinalIgnoreCase); // 不区分大小写

string lower = str2.ToLower(); // 转小写 → "helloworld"
string upper = str2.ToUpper(); // 转大写 → "HELLOWORLD"

int index1 = str2.IndexOf('o'); // 查找字符位置 → 4
int index2 = str2.LastIndexOf('o'); // 从后向前查找 → 6
int index3 = str2.IndexOf("World"); // 查找子串位置 → 5

string replaced = str2.Replace("World", "C#"); // 替换 → "HelloC#"
string removed1 = str2.Remove(5); // 删除位置5后的字符(包括5) → "Hello"
string removed2 = str2.Remove(5, 3); // 从位置5开始删除3个字符(包括5) → "Hellold"

string trimmed1 = str2.Trim(); // 去除两端空白
string trimmed2 = str2.TrimStart(); // 仅去除开头空白
string trimmed3 = str2.TrimEnd(); // 仅去除结尾空白

string[] sp = str2.Split('o'); // 以'o'进行分割得到 → ["Hell", "W", "rld"]

8. 方法

方法是 C# 编程中的基本构建块,用于封装可重用的代码逻辑。

public static int Add(int a, int b)
{
    return a + b;
}

(1)访问修饰符

  • public:无访问限制
  • private:仅当前类可访问(默认)
  • protected:当前类及派生类可访问
  • internal:同一程序集可访问
  • protected internal:同一程序集或派生类可访问

(2)方法参数

值参数:

  • 最常见的参数传递方式
  • 传递的是参数的副本
  • 方法内对参数的修改不会影响原始值
void ModifyValue(int x)
{
    x = 10; // 只修改副本
    Console.WriteLine(x); // 输出 10
}

int num = 5;
ModifyValue(num);
Console.WriteLine(num); // 输出 5 (原始值未改变)

引用参数:

  • 传递的是变量的引用(内存地址)
  • 方法内对参数的修改会影响原始值
  • 调用时必须初始化变量
void ModifyRef(ref int x)
{
    x = 10; // 修改原始变量
}

int num = 5;
ModifyRef(ref num);
Console.WriteLine(num); // 输出 10 (原始值已改变)

参数数组:

  • 允许传递可变数量的参数
  • 必须是方法的最后一个参数
  • 参数类型必须是一维数组
int Sum(params int[] numbers)
{
    return numbers.Sum();
}

var total = Sum(1, 2, 3, 4, 5); // 输出 15

(3)函数重载

函数重载(Function Overloading)是C#中一项重要的特性,它允许在同一个作用域内定义多个同名函数,但这些函数的参数列表必须不同。规则如下:

  1. 函数名必须相同
  2. 参数列表必须不同,可以是以下任意一种:
    • 参数数量不同
    • 参数类型不同
    • 参数顺序不同(当类型不同时)
  3. 返回类型可以相同也可以不同,但仅返回类型不同不足以构成重载
public class Calculator
{
    // 版本1:两个整数相加
    public int Add(int a, int b)
    {
        return a + b;
    }
    // 版本2:三个整数相加(参数数量不同)
    public int Add(int a, int b, int c)
    {
        return a + b + c;
    }
    // 版本3:两个双精度浮点数相加(参数类型不同)
    public double Add(double a, double b)
    {
        return a + b;
    }
    // 版本4:整数和双精度浮点数相加(参数顺序不同)
    public double Add(int a, double b)
    {
        return a + b;
    }
    // 版本5:双精度浮点数和整数相加(参数顺序不同)
    public double Add(double a, int b)
    {
        return a + b;
    }
    // 错误示例:仅返回类型不同,不能构成重载
    // public float Add(int a, int b) { return a + b; }
}

9. 递归

递归是编程中一种重要的技术,它指的是一个方法直接或间接调用自身的过程。在C#中,递归可以优雅地解决许多问题,特别是那些可以分解为相同子问题的情况。

递归由两个主要部分组成:

  1. 基准条件(Base Case)​​:递归终止的条件,防止无限递归
  2. 递归条件(Recursive Case)​​:方法调用自身的部分,通常问题规模比原问题小

斐波那契数列:

static int again(int n)
{
    // 基准条件
    if (n == 0)
    {
        return 2;
    }
    if (n == 1)
    {
        return 3;
    }
    // 递归条件
    return again(n - 1) + again(n - 2);
}

static void Main(string[] args)
{
    Console.WriteLine(again(40));
}

求阶乘:

static int again(int n)
{
    if ( n==1)
    {
        return 1;
    }
    return again(n - 1) * n;
}

static void Main(string[] args)
{
    Console.WriteLine(again(10));
}

10. 常量

在 C# 中,常量是指在程序运行期间其值不会改变的标识符。常量提供了一种使代码更易读、更易维护的方式,同时也提高了程序的性能。

public class MyClass
{
    // const 数据类型 常量名 = 值;
    public const double PI = 3.14159;
}

常量的特点:

  1. 必须在声明时初始化​:常量必须在定义时就赋值
  2. 值不可更改​:一旦定义后,其值在程序生命周期内不能改变
  3. 编译时常量​:const 常量的值在编译时就确定
  4. 隐式静态​:常量默认是静态的,可以通过类名直接访问
  5. 只能是内置类型​:const 常量只能是数值类型、bool、char 或 string

11. 枚举类型

枚举(Enum)是C#中一种特殊的值类型,它允许你定义一组命名的常量值。枚举使代码更易读、更易维护,因为它用有意义的名称替代了数字常量。

enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
}

枚举的特点:

  1. 强类型​:枚举是强类型,不能隐式转换为整数或其他类型
  2. 值类型​:枚举是值类型,存储在栈上
  3. 默认基础类型​:默认是int,但可以指定其他整数类型(byte, sbyte, short, ushort, int, uint, long, ulong)
  4. 默认值​:第一个枚举数的值为0,后续依次递增1

12. 结构体

结构体(struct)是C#中的一种值类型,用于封装小型相关变量的集合。结构体类似于类,但有一些重要区别。

public struct Point
{
    public int X;
    public int Y;
    // 自定义构造函数
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
    // 方法
    public void Move(int deltaX, int deltaY)
    {
        X += deltaX;
        Y += deltaY;
    }
}

结构体的特点:

  1. 值类型​:结构体是值类型,存储在栈上
  2. 继承​:不能继承其他结构或类,也不能被继承
  3. 默认构造函数​:总是有一个隐式的无参构造函数
  4. 性能​:适合小型数据结构,访问速度通常比类快

13. 委托

委托(Delegate)是 C# 中一种强大的功能,它本质上是一种类型安全的函数指针,允许将方法作为参数传递或存储为变量。委托是 .NET 中事件和回调机制的基础。

// 声明一个委托类型
public delegate void MyDelegate(string message);
// 定义与委托匹配的方法
public static void ShowMessage(string msg)
{
    Console.WriteLine(msg);
}
// 使用委托
MyDelegate del = new MyDelegate(ShowMessage);
del("Hello, Delegate!");

委托的特点:

  • 类型安全:委托在编译时检查类型安全性
  • 可以引用静态方法或实例方法
  • 支持多播(多个方法可以绑定到一个委托)
  • 是 .NET 事件模型的基础

你可能感兴趣的:(C#基础篇(01)一篇文章搞定C#基础语法)