在C++基础上理解CSharp-3

1:学习目标

  • 掌握 C# 所有流程控制语句的用法
  • 理解 C# 与 C++ 在流程控制上的关键差异
  • 熟练掌握 C# 方法的定义与调用
  • 深入理解ref/out/in参数的高级用法
  • 掌握方法重载、可选参数、命名参数等特性
  • 学会使用本地函数简化代码
  • 理解递归的原理和应用场景
  • 所有知识点均与 C++ 对应概念进行深度对比

2:条件语句

C#的条件语句和C++基本相同,但是有一些重要的增强和限制。

1:if-else语句

if-else语句的语法行为和C++完全一致

cs 复制代码
int score = 85;

if (score >= 90)
{
    Console.WriteLine("优秀");
}
else if (score >= 80)
{
    Console.WriteLine("良好");
}
else if (score >= 60)
{
    Console.WriteLine("及格");
}
else
{
    Console.WriteLine("不及格");
}

与C++的相同点:

  • 条件表达式必须返回布尔值
  • 支持嵌套 if-else
  • 大括号可以省略(但强烈不推荐)

与C++的不同点:

C# 不允许将整数直接作为条件表达式,必须显式转换为 bool

cs 复制代码
int x = 10;
// if (x) { } // 编译错误:无法将int转换为bool
if (x != 0) { } // 正确写法

2:switch语句

C# 的switch语句比 C++ 强大得多,支持更多的类型和模式匹配。

1:基本用法
cs 复制代码
int day = 3;

switch (day)
{
    case 1:
        Console.WriteLine("星期一");
        break;
    case 2:
        Console.WriteLine("星期二");
        break;
    case 3:
        Console.WriteLine("星期三");
        break;
    case 4:
        Console.WriteLine("星期四");
        break;
    case 5:
        Console.WriteLine("星期五");
        break;
    case 6:
    case 7:
        Console.WriteLine("周末");
        break;
    default:
        Console.WriteLine("无效的日期");
        break;
}

与 C++ 的关键区别:

  1. 不允许隐式穿透 :每个 case 块必须以breakreturnthrowgoto case结束,不能像 C++ 那样省略 break 导致自动穿透到下一个 case。
  2. 支持多个 case 标签共享同一个代码块:如上面的 case 6 和 case 7。
  3. 支持更多的类型:C# 的 switch 语句不仅支持整数类型,还支持字符串、枚举、char 等类型。
2:字符串switch

这是C#非常实用的特性,C++到了C++11才支持Switch可以用字符串

cs 复制代码
string language = "C#";

switch (language)
{
    case "C++":
        Console.WriteLine("C++是一种高性能的系统编程语言");
        break;
    case "C#":
        Console.WriteLine("C#是一种现代的通用编程语言");
        break;
    case "Java":
        Console.WriteLine("Java是一种跨平台的编程语言");
        break;
    default:
        Console.WriteLine("未知的编程语言");
        break;
}
3:模式匹配(C#7.0+)

C#7.0引入了模式匹配,使switch语句更加强大

cs 复制代码
object obj = "Hello World";

switch (obj)
{
    case int i:
        Console.WriteLine($"这是一个整数:{i}");
        break;
    case string s:
        Console.WriteLine($"这是一个字符串:{s},长度为{s.Length}");
        break;
    case double d when d > 0:
        Console.WriteLine($"这是一个正浮点数:{d}");
        break;
    case null:
        Console.WriteLine("这是一个null值");
        break;
    default:
        Console.WriteLine("这是一个未知类型");
        break;
}

模式匹配可以根据对象的类型、值甚至条件来进行分支,这是 C++ 的 switch 语句完全不具备的功能。

3:循环语句

C#提供了4种循环语句:for,while,do-while和foreach,前三种和C++相同,foreach是C#特有的

1:for循环

for循环的语法和C++一样

cs 复制代码
// 输出1到10
for (int i = 1; i <= 10; i++)
{
    Console.WriteLine(i);
}

// 多个初始化和增量表达式
for (int i = 0, j = 10; i < j; i++, j--)
{
    Console.WriteLine($"i={i}, j={j}");
}

2:while循环

while循环的语法和行为与 C++ 完全一致:

cs 复制代码
int i = 1;
while (i <= 10)
{
    Console.WriteLine(i);
    i++;
}

3:do-while循环

do-while的语法行为和C++完全一致:

cs 复制代码
int i = 1;
do
{
    Console.WriteLine(i);
    i++;
} while (i <= 10);

4:foreach循环

foreach循环是 C# 特有的,用于遍历集合或数组中的元素。它比传统的 for 循环更简洁、更安全。

cs 复制代码
int[] numbers = { 1, 2, 3, 4, 5 };

foreach (int num in numbers)
{
    Console.WriteLine(num);
}

// 遍历字符串中的每个字符
string message = "Hello C#";
foreach (char c in message)
{
    Console.WriteLine(c);
}

与C++的范围for对比

cpp 复制代码
int numbers[] = { 1, 2, 3, 4, 5 };
for (int num : numbers)
{
    std::cout << num << std::endl;
}

关键区别:

  1. C# 的foreach循环可以遍历任何实现了IEnumerable接口的类型,而 C++ 的范围 for 循环需要类型提供begin()end()方法。

  2. C# 的foreach循环是只读的,不能在循环体内修改集合元素:

cs 复制代码
int[] numbers = { 1, 2, 3, 4, 5 };
foreach (int num in numbers)
{
    // num = 10; // 编译错误:无法分配给foreach循环变量
}
cpp 复制代码
for (int& num : numbers)
{
    num = 10; // 正确
}

3.C# 的foreach循环不支持遍历过程中修改集合(添加或删除元素),否则会抛出InvalidOperationException异常。

4:跳转语句

1:break语句

break语句用于跳出最近的循环或 switch 语句,与 C++ 完全相同:

cs 复制代码
for (int i = 1; i <= 10; i++)
{
    if (i == 5)
    {
        break; // 跳出循环
    }
    Console.WriteLine(i);
}
// 输出:1 2 3 4

2:continue语句

continue语句用于跳过当前循环的剩余部分,进入下一次循环,与 C++ 完全相同:

cs 复制代码
for (int i = 1; i <= 10; i++)
{
    if (i % 2 == 0)
    {
        continue; // 跳过偶数
    }
    Console.WriteLine(i);
}
// 输出:1 3 5 7 9

3:goto语句

goto语句用于跳转到指定的标签位置。虽然很多人不推荐使用 goto,但在某些场景下(如跳出多层循环),goto 是最简洁的解决方案。(其实还是不怎么用)

cs 复制代码
// 使用goto跳出多层循环
for (int i = 0; i < 3; i++)
{
    for (int j = 0; j < 3; j++)
    {
        if (i == 1 && j == 1)
        {
            goto EndLoop; // 跳转到EndLoop标签
        }
        Console.WriteLine($"i={i}, j={j}");
    }
}
EndLoop:
Console.WriteLine("循环结束");

与 C++ 的相同点:

  • 语法和行为基本相同
  • 不能跳转到方法外部
  • 不能跳转到 try-catch 块内部

4:return语句

return语句用于从方法中返回,与 C++ 完全相同:

cs 复制代码
int Add(int a, int b)
{
    return a + b;
}

void PrintMessage(string message)
{
    Console.WriteLine(message);
    return; // 可以省略
}

5:方法系统(函数)

方法是 C# 中代码组织的基本单元,对应 C++ 中的函数。C# 的方法系统比 C++ 更强大,提供了更多的特性。

1:方法的定义与调用

方法的基本定义语法

cs 复制代码
[访问修饰符] [返回类型] 方法名([参数列表])
{
    // 方法体
    [return 返回值;]
}
//void 可以不要返回值
cs 复制代码
// 定义一个加法方法
public int Add(int a, int b)
{
    return a + b;
}

// 调用方法
int result = Add(10, 20);
Console.WriteLine(result); // 输出 30

与 C++ 函数的对比:

  • 语法基本相同
  • C# 的方法必须定义在类或结构体内部,不能定义全局函数
  • C# 的方法默认是private访问修饰符,而 C++ 的函数默认是全局的

2:方法参数详解

C# 提供了四种参数传递方式:值参数、ref参数、out参数和in参数。

1:值参数

这是默认的参数传递方式,传递的是参数的副本。上一篇博客已经详细讲解过:

  • 对于值类型:传递值的副本,方法内的修改不会影响原始变量
  • 对于引用类型:传递引用的副本,方法内对对象内容的修改会影响原始对象,但对引用本身的修改不会影响原始引用
2:ref参数

ref参数传递的是变量的引用,方法内对参数的任何修改都会影响原始变量。

高级用法:ref 返回和 ref 局部变量(C# 7.0+)

C# 7.0 引入了 ref 返回和 ref 局部变量,允许方法返回引用,并将引用存储在局部变量中:

cs 复制代码
internal class Program
{
    public static ref int GetArrayElementRef(int[] array, int index)
    {
        return ref array[index];
    }

    static void Main(string[] args)
    {
        // 定义一个返回数组元素引用的方法
    // 使用ref返回
    int[] numbers = { 1, 2, 3, 4, 5 };
    ref int element = ref GetArrayElementRef(numbers, 2);
    element = 100; // 修改数组元素
    Console.WriteLine(numbers[2]); // 输出 100
    }
}

这个特性在处理大数组时可以显著提高性能,避免了复制整个数组元素。C++ 中可以通过返回指针或引用来实现类似的功能。

3:out参数

out参数用于从方法中返回多个值。与ref参数不同,out参数在传入方法时不需要初始化,方法必须为其赋值。

高级用法:元组返回(C# 7.0+)

虽然out参数可以返回多个值,但 C# 7.0 引入的元组是更优雅的解决方案:

cs 复制代码
// 使用out参数返回多个值
public bool TryDivide(int dividend, int divisor, out int quotient, out int remainder)
{
    if (divisor == 0)
    {
        quotient = 0;
        remainder = 0;
        return false;
    }

    quotient = dividend / divisor;
    remainder = dividend % divisor;
    return true;
}

// 使用元组返回多个值(推荐)
public (bool success, int quotient, int remainder) TryDivideTuple(int dividend, int divisor)
{
    if (divisor == 0)
    {
        return (false, 0, 0);
    }

    return (true, dividend / divisor, dividend % divisor);
}

// 调用元组方法
var result = TryDivideTuple(10, 3);
if (result.success)
{
    Console.WriteLine($"商:{result.quotient},余数:{result.remainder}");
}

// 解构元组
var (success, quotient, remainder) = TryDivideTuple(10, 3);

元组返回比out参数更简洁、更易读,是 C# 中返回多个值的首选方式。

4:in参数

in参数用于传递只读引用,方法内不能修改参数的值。in参数主要用于避免大值类型的复制开销,提高性能。

cs 复制代码
// 定义一个大结构体
public struct LargeStruct
{
    public long Value1;
    public long Value2;
    public long Value3;
    public long Value4;
}

// 使用in参数传递,避免复制整个结构体
public void ProcessLargeStruct(in LargeStruct ls)
{
    // ls.Value1 = 100; // 编译错误:不能修改in参数
    Console.WriteLine(ls.Value1);
}

3:可选参数与命名参数

C# 4.0 引入了可选参数和命名参数,使方法调用更加灵活。

1:可选参数

可选参数在定义时指定默认值,调用时可以省略:

cs 复制代码
// 定义带有可选参数的方法
public void PrintMessage(string message, int times = 1)
{
    for (int i = 0; i < times; i++)
    {
        Console.WriteLine(message);
    }
}

// 调用方法
PrintMessage("Hello"); // 使用默认值1
PrintMessage("Hello", 3); // 指定times为3

注意:

  • 可选参数必须放在参数列表的最后
  • 默认值必须是编译时常量
  • 多个可选参数可以按顺序省略
2:命名参数

命名参数允许你通过参数名来指定参数值,而不需要按顺序:

cs 复制代码
// 定义一个方法
public void CreateUser(string name, int age, string email = "", bool isActive = true)
{
    Console.WriteLine($"Name: {name}, Age: {age}, Email: {email}, Active: {isActive}");
}

// 使用命名参数调用
CreateUser("张三", 25, isActive: false); // 只指定isActive参数,email使用默认值
CreateUser(age: 30, name: "李四"); // 不按顺序指定参数

命名参数在调用带有多个可选参数的方法时特别有用,可以提高代码的可读性。

4:方法重载

方法重载是指在同一个类中定义多个同名但参数列表不同的方法。C# 的方法重载规则与 C++ 基本相同:

cs 复制代码
// 方法重载
public int Add(int a, int b)
{
    return a + b;
}

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

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

// 调用不同的重载方法
Console.WriteLine(Add(10, 20)); // 调用第一个Add
Console.WriteLine(Add(10.5, 20.5)); // 调用第二个Add
Console.WriteLine(Add(10, 20, 30)); // 调用第三个Add

与 C++ 的不同点:

C# 中refout可以作为重载的依据,而in不行:

cs 复制代码
public void Method(int x) { }
public void Method(ref int x) { } // 合法重载
public void Method(out int x) { } // 合法重载
public void Method(in int x) { } // 编译错误:与第一个Method冲突

C# 不允许仅返回值不同的重载。

5:静态方法与实例方法

  • 静态方法:属于类本身,不需要创建类的实例就可以调用
  • 实例方法:属于类的实例,必须创建类的实例后才能调用
cs 复制代码
public class Calculator
{
    // 静态方法
    public static int Add(int a, int b)
    {
        return a + b;
    }

    // 实例方法
    public int Subtract(int a, int b)
    {
        return a - b;
    }
}

// 调用静态方法
int sum = Calculator.Add(10, 20);

// 调用实例方法
Calculator calc = new Calculator();
int difference = calc.Subtract(20, 10);

这与 C++ 的静态成员函数和普通成员函数完全相同。

6:本地函数(C#7.0)

本地函数是定义在方法内部的函数,只能在包含它的方法内部调用。本地函数是 C# 特有的特性,C++ 没有直接对应的概念。

cs 复制代码
public int CalculateFactorial(int n)
{
    if (n < 0)
    {
        throw new ArgumentException("n不能为负数");
    }

    // 定义本地函数
    int Factorial(int x)
    {
        if (x == 0)
        {
            return 1;
        }
        return x * Factorial(x - 1);
    }

    return Factorial(n);
}

// 调用方法
Console.WriteLine(CalculateFactorial(5)); // 输出 120

本地函数的优点:

  1. 封装性:只能在包含它的方法内部访问
  2. 可以访问包含方法的局部变量和参数
  3. 比 lambda 表达式更高效,支持递归
  4. 代码更清晰,将辅助函数与主函数放在一起

7:递归

递归是指方法调用自身的编程技巧。C# 的递归与 C++ 完全相同:

cs 复制代码
// 计算斐波那契数列
public int Fibonacci(int n)
{
    if (n <= 1)
    {
        return n;
    }
    return Fibonacci(n - 1) + Fibonacci(n - 2);
}

// 调用
Console.WriteLine(Fibonacci(10)); // 输出 55

注意:

  • 递归必须有终止条件,否则会导致栈溢出
  • 递归深度过大会导致StackOverflowException异常
  • 对于深度较大的递归,建议使用迭代代替

6:总结

在这篇博客中,我们深入学习了 C# 的流程控制语句和方法系统。对于 C++ 开发者来说,大部分概念都是熟悉的,但 C# 提供了很多增强特性:

  • switch 语句支持字符串和模式匹配
  • foreach 循环提供了简洁安全的集合遍历方式
  • 方法支持可选参数和命名参数
  • 本地函数可以更好地封装辅助逻辑
  • 元组返回提供了优雅的多值返回方式

这些特性可以显著提高代码的可读性和开发效率。

相关推荐
techdashen1 小时前
Rust 能帮你捕获什么,又不能捕获什么
开发语言·后端·rust
Geometry Fu1 小时前
《设计模式》2026编程作业汇总
java·c++·设计模式
吃好睡好便好1 小时前
在Matlab中绘制柱面图
开发语言·学习·算法·matlab
Anastasiozzzz1 小时前
深度解析 AI 时代的“TCP/IP协议”:Agent-to-Agent (A2A) 通信架构与多智能体协同底层逻辑
大数据·开发语言·网络·数据库·网络协议·tcp/ip·架构
ChoSeitaku1 小时前
02.变量_数据类型转换_运算符
java·大数据·开发语言
AI科技星1 小时前
基于全域数学0-1-∞体系的1.237宇宙临界常数及时空超导统一理论
c语言·开发语言·线性代数·量子计算·agi
Arman_1 小时前
Rust 接入阿里云 OSS 断点上传下载:rusty-cat 直连模式实战
开发语言·阿里云·rust·oss断点续传
十五年专注C++开发1 小时前
QtFluentWidgets: 一套基于C++ Qt Widgets的Fluent Design风格控件库
开发语言·c++·qt·qtfluentwidgets
江屿风2 小时前
【c++笔记】类和对象流食般投喂(下)
开发语言·c++·笔记