📂 C# / C#基础📂 Unity3D / Unity3D基础📂 Godot#计算机#C##程序设计#.NET#Unity3D#Godot#游戏开发

C#基础语法:从入门到放弃(不是)

⏱️ 8 min read📝 1481 words

1. 认识C#世界

1.1. 什么是.NET?

想象一下,.NET就像是一个巨大的乐高积木城堡,由Microsoft精心设计并慷慨开源。这个平台让你能够使用多种语言(C#、F#或Visual Basic)、各种编辑器和丰富的库来构建各类应用程序。无论是Web、移动、桌面、游戏还是物联网应用,.NET都能帮你搞定。

.NET不仅仅是一个框架,它是一个完整的生态系统,提供了跨平台支持、一致的API、丰富的库(通过NuGet包管理器,这里有超过10万的软件包)以及多种应用模型。

小贴士:当你在编写C#代码时,其实你是在.NET这个大舞台上表演,而C#只是你选择的表演语言。

1.2. 什么是C#?

C#(读作"C-Sharp")是一种简单、现代、面向对象且类型安全的编程语言。它是.NET平台上最受欢迎的语言,特别适合游戏开发(Unity和Godot引擎都使用C#)。

想象C#是一把瑞士军刀:它锋利(性能好)、多功能(支持多种编程范式)、而且设计精良(语法简洁明了)。它不像C++那样复杂,也不像Python那样随意,它找到了一个完美的平衡点。

1.3. .NET交互模式

在.NET世界中有两种主要的交互模式:

  1. C/S模式(客户端/服务器):需要安装专门的客户端软件,如桌面应用程序。就像你必须下载微信才能和朋友聊天一样。
  2. B/S模式(浏览器/服务器):只需要一个浏览器,如Web应用程序。就像直接通过网页版微信聊天,无需安装任何软件。

1.4. 开发工具

Visual Studio是C#开发的首选IDE(集成开发环境),它就像程序员的驾驶舱,提供了代码编辑、调试、测试、部署等全方位功能。Visual Studio 2022是最新版本,支持64位架构,性能更佳。

有趣事实:Visual Studio有时候占用的内存比你的整个应用程序还要多,但它提供的生产力提升绝对值得这些资源消耗!

2. C#语法基础

2.1. C#程序的一般结构

using System; // 引入命名空间,就像Python的import

namespace YourNamespace // 命名空间,防止名称冲突
{
    class YourClass // 类定义,C#中一切皆对象
    {
        // 程序入口点
        static void Main(string[] args)
        {
            Console.WriteLine("Hello world!"); // 输出到控制台
            Console.ReadKey(); // 等待按键,防止控制台闪退
        }
    }
}

C#代码规则:

  • 所有标点符号必须是英文半角
  • 每行代码以分号(;)结束
  • 代码区分大小写
  • 使用大括号{}定义代码块

2.2. 注释:与未来的自己对话

代码是写给人看的,只是恰好能让机器执行。好的注释能让你在六个月后不至于对着自己的代码问:"这是我写的?我当时在想什么?"

C#支持三种注释:

  1. 单行注释:用//开头

    int a = 10; // 定义一个整数变量a
    
  2. 多行注释:用/*开头,*/结尾

    /*
    这是一个多行注释
    可以跨越多行
    */
    
  3. XML文档注释:用///开头,为代码生成文档

    /// <summary>
    /// 计算两个数的和
    /// </summary>
    /// <param name="a">第一个数</param>
    /// <param name="b">第二个数</param>
    /// <returns>两数之和</returns>
    public int Add(int a, int b)
    {
        return a + b;
    }
    

2.3. 基本数据类型

C#中的变量分为三类:值类型、引用类型和指针类型。理解这些类型的区别,就像理解现实世界中不同物体的性质一样重要。

值类型

值类型直接包含数据,存储在栈中。它们是从System.ValueType派生的。常见的值类型包括:

关键字 描述 范围 默认值
sbyte 8位有符号整数 -128 到 127 0
short 16位有符号整数 -32,768 到 32,767 0
int 32位有符号整数 -2,147,483,648 到 2,147,483,647 0
long 64位有符号整数 -9.2e18 到 9.2e18 0L
byte 8位无符号整数 0 到 255 0
ushort 16位无符号整数 0 到 65,535 0
uint 32位无符号整数 0 到 4.2e9 0
ulong 64位无符号整数 0 到 1.8e19 0
float 32位单精度浮点数 ±1.5e-45 到 ±3.4e38 0.0F
double 64位双精度浮点数 ±5.0e-324 到 ±1.7e308 0.0D
decimal 128位高精度小数 ±1.0e-28 到 ±7.9e28 0.0M
char 16位Unicode字符 U+0000 到 U+ffff '\0'
bool 布尔值 true 或 false false

有趣的事实:为什么float用F后缀,double用D后缀,decimal用M后缀?F代表Float,D代表Double,而M代表Money(钱),因为decimal最适合处理财务数据!

引用类型

引用类型不直接包含数据,而是存储对数据的引用(地址)。它们存储在堆中。主要的引用类型包括:

  1. Object类型:所有类型的基类
  2. String类型:字符串
  3. Dynamic类型:动态类型,运行时解析

指针类型

指针类型用于不安全代码,直接操作内存地址。在大多数C#应用中很少使用,除非需要高性能或与非托管代码交互。

类型转换

C#中类型转换有两种:隐式转换(自动)和显式转换(强制)。

隐式转换:当目标类型能安全容纳源类型时自动发生

int i = 10;
double d = i; // 隐式转换,安全

显式转换:当可能存在数据丢失时需要强制转换

double d = 10.5;
int i = (int)d; // 显式转换,结果为10,小数部分丢失

对于不兼容类型,使用Convert类:

string s = "123";
int i = Convert.ToInt32(s);

2.4. 运算符:C#的调味料

算术运算符

运算符 描述 例子
+ 加法 a + b
- 减法 a - b
* 乘法 a * b
/ 除法 a / b
% 取模 a % b
++ 自增 a++ 或 ++a
-- 自减 a-- 或 --a

注意:++a(前置)先增加后使用,a++(后置)先使用后增加。在性能关键代码中,前置通常比后置更高效,因为后置需要创建临时变量。

关系运算符、逻辑运算符、位运算符

这些运算符用于比较值、进行逻辑操作和位级操作。熟练掌握它们就像厨师熟练使用各种调料一样,能让代码更加精准、高效。

赋值运算符

除了基本赋值(=),C#还提供复合赋值运算符如+=, -=, *=, /=等,让代码更简洁。

特殊运算符

  • sizeof():获取类型大小
  • typeof():获取类型信息
  • ?: 三元条件运算符
  • ?? 空值合并运算符
  • ?. 空值条件运算符
  • => Lambda运算符(用于创建匿名函数)

2.5. 特殊字符:C#的暗语

转义字符

使用反斜杠()改变字符的正常含义:

  • \n:换行
  • \t:制表符
  • ":双引号
  • \:反斜杠
  • 等等

@符号

在字符串前加@,取消转义字符的特殊含义,保留原始格式:

string path = @"C:\Users\Documents\file.txt"; // 不需要双反斜杠

字符串插值

使用$符号,让字符串内部直接嵌入变量:

string name = "张三";
int age = 25;
Console.WriteLine($"你好,{name}! 你今年{age}岁了。");

比传统的字符串拼接或占位符更直观、易读。

2.6. 可空类型:给值类型赋予"无"的能力

值类型如int、bool等,默认不能为null。但有时候我们确实需要表示"无值"状态。C#提供了可空类型:

int? nullableInt = null; // ?表示可空
bool? flag = null;

// 空值合并运算符
int value = nullableInt ?? 0; // 如果nullableInt为null,则使用0

3. 流程控制的艺术

编程不只是定义变量和方法,还需要控制程序的执行流程。C#提供了丰富的流程控制语句,让你能够指挥代码如指挥交响乐团。

3.1. 选择结构:人生的十字路口

if/else语句

if (temperature > 30)
{
    Console.WriteLine("今天真热!");
}
else if (temperature > 20)
{
    Console.WriteLine("天气真宜人。");
}
else
{
    Console.WriteLine("有点冷,多穿点。");
}

switch语句

C# 8.0+的switch表达式更加简洁:

string GetWeatherDescription(int temperature) =>
    temperature switch
    {
        > 30 => "真热",
        > 20 => "宜人",
        > 10 => "有点凉",
        _ => "真冷" // 默认情况
    };

三目运算符

当if/else太重,而你又只需要简单条件赋值时:

string weather = temperature > 25 ? "热" : "不热";

3.2. 循环结构:编程中的复读机

for循环

当你知道需要循环的确切次数时:

for (int i = 0; i < 10; i++)
{
    Console.WriteLine($"第{i+1}次循环");
}

while和do-while循环

当你不确定循环次数,只知条件时:

// 先判断,后执行
while (condition)
{
    // 循环体
}

// 先执行,后判断
do
{
    // 循环体
} while (condition);

循环控制

  • break:立即终止整个循环
  • continue:跳过当前迭代,进入下一次循环
  • return:从方法中返回,终止所有循环

警告:避免在多层嵌套循环中过度使用break,它可能会让代码逻辑变得难以追踪。有时候,将循环重构为方法,用return代替break会更清晰。

4. 复杂数据类型

当你需要处理更复杂的数据结构时,C#提供了多种选择。

4.1. 字符串:不可变的字符序列

C#中的字符串(String)是引用类型,且不可变(每次修改都会创建新字符串)。这看似低效,但.NET的字符串池化机制使其非常高效。

常用字符串操作:

  • 拼接:+string.Concat()
  • 格式化:string.Format() 或 字符串插值
  • 比较:==Equals()Compare()
  • 搜索:IndexOf()Contains()StartsWith()EndsWith()
  • 修改:Substring()Replace()Trim()ToUpper()/ToLower()

有趣的事实:字符串不可变性带来了线程安全和哈希码缓存等好处,虽然在频繁修改字符串时可能需要使用StringBuilder

4.2. 数组:相同类型的有序集合

C#中的数组是引用类型,具有固定大小:

// 声明和初始化
int[] numbers = new int[5]; // 长度为5的整数数组
string[] names = { "张三", "李四", "王五" }; // 初始化时指定元素

// 多维数组
int[,] matrix = new int[3, 3]; // 3x3二维数组
int[,,] cube = new int[2, 3, 4]; // 三维数组

// 交错数组(数组的数组)
int[][] jaggedArray = new int[3][];
jaggedArray[0] = new int[2];
jaggedArray[1] = new int[3];
jaggedArray[2] = new int[4];

4.3 枚举:命名的整数常量

枚举让代码更具可读性,避免"魔法数字":

enum Weekday
{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}

Weekday today = Weekday.Friday;
Console.WriteLine($"今天是{today}"); // 输出:今天是Friday

// 使用整数值
Console.WriteLine((int)today); // 输出:4(从0开始)

4.4. 结构体:轻量级的类

结构体(struct)是值类型,适合表示轻量级数据结构:

public struct Point
{
    public int X;
    public int Y;
    
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
    
    public double DistanceToOrigin()
    {
        return Math.Sqrt(X * X + Y * Y);
    }
}

Point p = new Point(3, 4);
Console.WriteLine(p.DistanceToOrigin()); // 输出:5

与类相比,结构体:

  • 是值类型,存储在栈上(除非装箱)
  • 不支持继承
  • 适合小型、不可变的数据
  • 默认构造函数不可重写

5. 方法与函数

方法是执行特定任务的代码块,是C#程序的基本构建单元。

5.1. 方法定义

[访问修饰符] [static] 返回类型 方法名(参数列表)
{
    // 方法体
    return 返回值; // 如果返回类型不是void
}

示例:

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

5.2. 参数传递

C#支持多种参数传递方式:

  1. 值参数(默认):传递值的副本
  2. 引用参数(ref):传递变量的引用
  3. 输出参数(out):用于返回多个值
  4. 参数数组(params):接受可变数量的参数
// ref参数
void Increment(ref int number)
{
    number++;
}

// out参数
bool TryParseInt(string input, out int result)
{
    return int.TryParse(input, out result);
}

// params参数
int Sum(params int[] numbers)
{
    int total = 0;
    foreach (int num in numbers)
    {
        total += num;
    }
    return total;
}

// 调用
int x = 5;
Increment(ref x); // x变为6

int parsedValue;
bool success = TryParseInt("123", out parsedValue); // parsedValue为123,success为true

int total = Sum(1, 2, 3, 4, 5); // total为15

5.3. 匿名方法与Lambda表达式

C#支持不具名的方法,用于简短的操作或作为委托:

// 匿名方法
Action<string> greet = delegate(string name)
{
    Console.WriteLine($"Hello, {name}!");
};

// Lambda表达式
Action<string> greet = name => Console.WriteLine($"Hello, {name}!");

// 带多个语句的Lambda
Func<int, int, int> add = (a, b) => 
{
    Console.WriteLine($"Adding {a} and {b}");
    return a + b;
};

Lambda表达式极大地简化了函数式编程和LINQ查询,是现代C#编程的核心特性。

6. 面向对象编程

C#是面向对象的语言,一切皆对象。理解OOP原则是掌握C#的关键。

6.1. 类与对象

类是蓝图,对象是实例:

public class Car
{
    // 字段(通常私有)
    private string _model;
    private int _year;
    
    // 属性(提供对字段的受控访问)
    public string Model
    {
        get { return _model; }
        set { _model = value; }
    }
    
    // 自动实现的属性(C# 3.0+)
    public string Color { get; set; }
    
    // 构造函数
    public Car(string model, int year)
    {
        _model = model;
        _year = year;
    }
    
    // 方法
    public void StartEngine()
    {
        Console.WriteLine($"{_model}'s engine is starting...");
    }
}

// 创建对象
Car myCar = new Car("Tesla Model S", 2023);
myCar.Color = "Red";
myCar.StartEngine();

6.2. 封装:数据保护的艺术

封装是隐藏对象的内部状态,只通过公共方法暴露必要的功能。通过访问修饰符实现:

修饰符 访问级别
public 任何地方
private 仅在类内部(默认)
protected 类内部和派生类
internal 同一程序集内
protected internal 同一程序集内或派生类

属性是封装字段的最佳实践:

private decimal _salary;
public decimal Salary
{
    get { return _salary; }
    set { _salary = value >= 0 ? value : 0; } // 验证逻辑
}

6.3. 继承:代码复用的利器

继承允许一个类(子类)获取另一个类(父类)的特性:

public class Vehicle
{
    public string Brand { get; set; }
    public void Start()
    {
        Console.WriteLine($"{Brand} vehicle is starting");
    }
}

public class Car : Vehicle // Car继承自Vehicle
{
    public int NumberOfDoors { get; set; }
    
    // 重写父类方法
    public new void Start()
    {
        base.Start(); // 调用父类方法
        Console.WriteLine("Car engine is warming up");
    }
}

里氏替换原则

子类应该能替换父类而不改变程序行为。C#提供isas操作符进行类型检查和安全转换:

Vehicle myVehicle = new Car();
if (myVehicle is Car)
{
    Car myCar = (Car)myVehicle; // 显式转换
    // 或者
    Car myCar2 = myVehicle as Car; // 安全转换,失败返回null
}

6.4. 多态:一物多形

多态允许不同类的对象对同一消息做出响应。C#通过三种方式实现多态:

  1. 虚方法:父类定义虚方法,子类可选择重写
  2. 抽象类:强制子类实现特定方法
  3. 接口:定义合同,实现类必须遵守
// 虚方法
public class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing a generic shape");
    }
}

public class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
}

// 抽象类
public abstract class Animal
{
    public abstract void MakeSound(); // 没有实现
}

public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Woof!");
    }
}

// 接口
public interface ILoggable
{
    void Log(string message);
}

public class DatabaseLogger : ILoggable
{
    public void Log(string message)
    {
        // 将消息记录到数据库
    }
}

6.5. 特殊类类型

C#提供了一些特殊的类类型来满足特定需求:

  1. 部分类(partial):将一个类的定义分散在多个文件中

    public partial class MyClass { /* 部分1 */ }
    public partial class MyClass { /* 部分2 */ }
    
  2. 密封类(sealed):禁止被继承

    public sealed class FinalClass { /* 不能被继承 */ }
    
  3. 静态类(static):不能实例化,只能包含静态成员

    public static class MathHelper
    {
        public static double Square(double x) => x * x;
    }
    

6.6. 关键字:this与base

  • this:引用当前实例

    public class Person
    {
        private string name;
        public Person(string name)
        {
            this.name = name; // 区分字段和参数
        }
    }
    
  • base:访问基类成员

    public class Child : Parent
    {
        public Child() : base() // 调用父类构造函数
        {
            base.SomeMethod(); // 调用父类方法
        }
    }
    

7. 写在最后

C#是一门强大而优雅的语言,它结合了C++的强大和Java的简洁,同时增添了独特的现代特性。从基础语法到面向对象编程,再到高级功能如LINQ、异步编程等,C#提供了一个完整的工具箱来解决各种编程挑战。

记住,成为一名优秀的C#开发者不在于记住所有语法,而在于理解核心概念和设计原则。当你面对问题时,不要问"在C#中如何实现这个",而要问"如何用面向对象的方式解决这个问题"。

祝你在C#之旅中一路顺风!记住,每个大师都曾是初学者,每个bug都是成长的机会。编码愉快!


最后更新:

💬 评论

评论系统接入中...