前言

作为一名编程新手,我选择了C#作为入门语言。两周的学习过程虽然充满挑战,但每当看到控制台输出预期的结果,或是自己写的小程序成功运行,那种成就感足以抵消所有困惑。本文将用通俗易懂的方式,把这两周的核心知识点串联起来,帮助后来者少走弯路。

一、初识C#与.NET世界

1.1 什么是程序?什么是软件?

在写第一行代码之前,我们需要理解两个最基础的概念:

  • 程序:为了完成某件事情而编写的一段有序的指令的集合
  • 软件:程序的集合,是可以在计算机上运行的电脑程序

简单来说,写程序就是用计算机能听懂的语言,教它帮我们做事。就像我们用中文教人做事一样,只不过对象换成了计算机。

1.2 计算机语言的三代演变

语言类型 特点 代表
机器语言 直接用01二进制控制硬件 01010011...
汇编语言 用特殊字符代替指令,需要翻译 MOV、ADD等
高级语言 接近人类语言,简洁明了 C#、Java、C++

C#(读作"C Sharp")正是高级语言的代表之一。

1.3 .NET与C#的关系

初学者最容易混淆的两个概念:

C#是一门编程语言,.NET是一个开发平台。 .NET平台支持C#、F#、VB.NET等多种语言,而C#是其中应用最广泛的语言。

.NET Framework和.NET Core/.NET 5+的主要区别:

特性 .NET Framework .NET (Core/5+)
平台支持 仅Windows Windows、Linux、macOS跨平台
性能 较老 显著优化,尤其Web领域
适用场景 维护旧项目 新项目首选

结论:新项目选.NET 6/7/8,旧项目维护用.NET Framework。

1.4 第一个C#程序:Hello World

using System;

namespace MyFirstApp
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            Console.ReadLine(); // 让控制台停留,等待按键
        }
    }
}

运行效果:控制台窗口输出"Hello World!"

2.3 类型转换

💡 新手提示Console.WriteLine()输出后换行,Console.Write()输出后不换行。

二、:C#基本语法

2.1 常量与变量

类型 特点 示例
常量 值不可改变 const double PI = 3.14;
变量 值可以改变 int age = 18;

标识符命名规则

  • 由字母、数字、下划线组成

  • 首字符不能是数字

  • 不能使用关键字

  • 区分大小写

  • 见名知意原则

2.2 数据类型体系

值类型与引用类型

  • 值类型:直接存储数据(int、double、bool、char等)

  • 引用类型:存储数据的引用地址(string、数组、类等)

常用数据类型速查表

类型 关键字 字节数 取值范围
字节型 sbyte 1 -128 ~ 127
短整型 short 2 -32768 ~ 32767
整型 int 4 -21亿 ~ 21亿
长整型 long 8 约±9.22×10¹⁸
单精度浮点 float 4 7位精度
双精度浮点 double 8 15-16位精度
高精度小数 decimal 16 28-29位精度
布尔型 bool 1 true/false
字符型 char 2 Unicode字符
字符串 string 引用类型 任意文本
// 自动转换(小→大)
double num = 10;  // int自动转为double

// 强制转换(大→小)
int a = (int)5.5;  // 结果为5,舍弃小数

// Parse转换(字符串→数值)
int b = int.Parse("123");

// Convert通用转换
double c = Convert.ToDouble("3.14");

2.4 运算符核心

算术运算符+ - * / %

int a = 10, b = 3;
Console.WriteLine(a + b);  // 13(加法)
Console.WriteLine(a - b);  // 7(减法)
Console.WriteLine(a / b);  // 3(整数除法取整)
Console.WriteLine(a % b);  // 1(取余)

逻辑运算符与短路效应

符号 名称 短路特性
&& 短路与 左边为false时,右边不执行
|| 短路或 左边为true时,右边不执行
& 逻辑与 两边都执行
| 逻辑或 两边都执行

💡 开发建议:优先使用&&||,效率更高。

bool a = false, b = true;

Console.WriteLine(a && b);  // 短路与 → False
Console.WriteLine(a || b);  // 短路或 → True
Console.WriteLine(a & b);   // 逻辑与 → False
Console.WriteLine(a | b);   // 逻辑或 → True

三、流程控制与循环

3.1 分支结构

if语句三种格式

// 格式1:单分支
if (条件) { 语句体; }

// 格式2:双分支
if (条件) { 语句体1; } 
else { 语句体2; }

// 格式3:多分支
if (条件1) { 语句体1; }
else if (条件2) { 语句体2; }
else { 语句体n; }

示例:

int score = 85;

// 单分支(输出及格)
if (score >= 60) {Console.WriteLine("及格");}

// 双分支(输出通过)
if (score >= 60) {Console.WriteLine("通过");}
else {Console.WriteLine("不通过");}

// 多分支(输出B)
if (score >= 90) {Console.WriteLine("A");}
else if (score >= 70) {Console.WriteLine("B");}
else if (score >= 60) {Console.WriteLine("C");}
else {Console.WriteLine("D");}

switch语句(适合等值判断):

switch (表达式)
{
    case 值1:
        语句体1;
        break;
    case 值2:
        语句体2;
        break;
    default:
        语句体n;
        break;
}

示例:

int day = 3;
switch (day)
{
    case 1: Console.WriteLine("周一"); break;
    case 2: Console.WriteLine("周二"); break;
    case 3: Console.WriteLine("周三"); break;//输出周三
    default: Console.WriteLine("其他"); break;
}

⚠️ 注意:每个case后面必须有breakdefault可以放在任何位置但建议放最后。

3.2 三种循环结构

// while循环:先判断后执行
int i = 1;
while (i <= 10)
{
    Console.WriteLine(i);
    i++;
}

// do-while循环:先执行后判断(至少执行一次)
int j = 1;
do
{
    Console.WriteLine(j);
    j++;
} 
while (j <= 10);

// for循环:最常用,计数精准
for (int k = 1; k <= 10; k++)
{
    Console.WriteLine(k);
}
//三种输出结果都是12345678910

跳转控制

  • break:跳出整个循环

  • continue:跳过本次循环,进入下一次

四、数组——批量数据的容器

4.1 数组的定义与初始化

// 动态初始化(指定长度,系统赋默认值0)
int[] arr1 = new int[5];  // {0,0,0,0,0}

// 静态初始化(直接赋值)
int[] arr2 = new int[] { 1, 3, 5, 7, 9 };
int[] arr3 = { 1, 3, 5, 7, 9 };  // 简化写法

4.2 数组核心操作

int[] arr1 = { 1, 3, 5, 7, 9 };
// 遍历数组(输出结果13579)
for (int i = 0; i < arr1.Length; i++)
{
    Console.WriteLine(arr1[i]);
}
//编程语言中它们的下标为:0,1,2,3,4

int[] arr2 = { 1, 3, 5, 7, 9 };
// 求最大值
int max = arr2[0];
for (int i = 1; i < arr2.Length; i++)
{
    if (arr2[i] > max) max = arr2[i];
}
Console.WriteLine(max);//输出9

// 数组反转(交换思想)
int[] arr3 = { 1, 3, 5, 7, 9 };
int start = 0, end = arr3.Length - 1;
while (start < end)
{
    int a = arr3[start];
    arr3[start] = arr3[end];
    arr3[end] = a;
    start++;
    end--;
}
for (int i = 0; i < arr1.Length; i++)
{
    Console.WriteLine(arr3[i]);
}
//输出97531

4.3 冒泡排序(经典算法)

int[] arr = { 3, 9, 7, 2, 6 };
for (int i = 0; i < arr.Length - 1; i++)      // 外层:排序轮数
{
    for (int j = 0; j < arr.Length - 1 - i; j++)  // 内层:比较次数
    {
        if (arr[j] > arr[j + 1])
        {
            int a = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = a;
        }
    }
}
//结果97632

4.4 二维数组

// 矩形二维数组(每行长度相同)
int[,] matrix = new int[3, 4];
int rows = matrix.GetLength(0);  // 行数
int cols = matrix.GetLength(1);  // 列数

// 锯齿数组(每行长度可以不同)
int[][] jagged = new int[3][];
jagged[0] = new int[] { 1, 2, 3 };
jagged[1] = new int[] { 4, 5 };
jagged[2] = new int[] { 6, 7, 8, 9 };

五、方法——功能的封装

5.1 方法的基本结构

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

四种方法类型

类型 示例
无参无返 static void SayHello() { }
无参有返 static int GetNumber() { return 10; }
有参无返 static void Print(string msg) { }
有参有返 static int Add(int a, int b) { return a + b; }

5.2 参数传递进阶

// out参数:返回多个不同类型的值
public static void Calculate(int[] nums, out int max, out int min)
{
    max = nums[0];
    min = nums[0];
    // ... 计算逻辑
}

// ref参数:引用传递,可修改原变量
public static void AddMoney(ref double salary)
{
    salary += 500;
}

// params可变参数:接收任意数量同类型参数
public static void Sum(string name, params int[] scores)
{
    int total = 0;
    foreach (int s in scores) total += s;
    Console.WriteLine($"{name}总成绩:{total}");
}
// 调用:Sum("张三", 90, 85, 88, 92);

5.3 方法重载(Overload)

重载条件:同一类中,方法名相同,参数列表不同(个数、类型、顺序)。

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

六、面向对象入门

6.1 面向过程 vs 面向对象

面向过程 面向对象
强调"怎么做"(动作) 强调"谁来做"(对象)
代码与具体执行者绑定 代码通用,屏蔽差异
示例:张三打开冰箱门 示例:冰箱门可以被打开

6.2 类与对象

:对象的模板/蓝图,定义对象应有的属性和行为。
对象:类的具体实例,在内存中真实存在。

// 定义类
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 
        { 
            if (value >= 0 && value <= 150) _age = value;
        }
    }
    
    // 方法
    public void Introduce()
    {
        Console.WriteLine($"我叫{_name},今年{_age}岁");
    }
}

// 使用对象
Person p = new Person();
p.Name = "小明";
p.Age = 18;
p.Introduce();//输出“我叫小明,今年18岁”

6.3 属性(Property)的本质

属性本质上就是两个方法:get(取值)和set(赋值),用于保护字段,对赋值和取值进行逻辑控制。

6.4 访问修饰符

修饰符 同类内 子类中 外部类
private
protected
public

七、封装与构造方法

7.1 构造方法(构造函数)

作用:初始化对象,在创建对象时为属性赋初始值。

特点

  1. 方法名与类名完全相同

  2. 没有返回值,连void也不能写

  3. 创建对象时自动调用

  4. 可以重载(多个参数不同的构造方法)

public class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    // 无参构造
    public Student() { }
    
    // 有参构造
    public Student(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

// 使用
Student s1 = new Student();                // 调用无参构造
Student s2 = new Student("张三", 18);      // 调用有参构造

7.2 this关键字

  1. 区分成员变量和局部变量this.name = name;

  2. 在构造方法中调用其他构造方法this(name, age, '男')

public Student(string name) : this(name, 0, '男') { }
public Student(string name, int age, char sex) 
{
    this.Name = name;
    this.Age = age;
    this.Sex = sex;
}

7.3 static静态成员

静态成员:属于类本身,所有对象共享,通过类名访问。

特点 静态成员 实例成员
归属 属于类 属于对象
调用方式 类名.成员 对象.成员
加载时机 类加载时 对象创建时
生命周期 类存在则存在 对象存在则存在
public class Counter
{
    public static int Count;      // 静态变量,所有对象共享
    public string Name;           // 实例变量,每个对象独有
    
    public Counter(string name)
    {
        Name = name;
        Count++;                  // 每创建一个对象,计数+1
    }
}

八、继承——代码复用的利器

8.1 继承的核心概念

语法class 子类 : 父类 { }

继承的特性

  1. 单根性:一个类只能有一个直接父类

  2. 传递性:孙子类继承父类的父类的所有非私有成员

public class Animal
{
    public string Name { get; set; }
    public void Eat() { Console.WriteLine("吃东西"); }
}

public class Dog : Animal
{
    public void Bark() { Console.WriteLine("汪汪汪"); }
}

public class GoldenRetriever : Dog
{
    public void Swim() { Console.WriteLine("会游泳"); }
}

// GoldenRetriever 拥有:Name、Eat()、Bark()、Swim()

8.2 继承中的成员访问

同名变量/方法的隐藏

class Father { public int age = 50; }
class Son : Father 
{ 
    new public int age = 20;  // 隐藏父类的age
    public void Test() 
    {
        Console.WriteLine(age);        // 20(子类自己的)
        Console.WriteLine(base.age);   // 50(父类的)
    }
}

8.3 继承中的构造方法

子类不会继承父类的构造方法,但子类构造方法会默认调用父类的无参构造方法。

如果父类只有有参构造方法,子类必须显式调用:

public class Person
{
    public string Name { get; set; }
    public Person(string name) { Name = name; }  // 只有有参构造
}

public class Student : Person
{
    public int Id { get; set; }
    public Student(string name, int id) : base(name)  // 必须显式调用父类构造
    {
        Id = id;
    }
}

九、抽象类与多态

9.1 抽象类(abstract)

使用场景:父类只知道子类"应该有"某个方法,但无法确定具体实现。

public abstract class Animal
{
    public string Name { get; set; }
    
    // 抽象方法:只有声明,没有方法体
    public abstract void Eat();
    
    // 非抽象方法:有具体实现
    public void Sleep() { Console.WriteLine("睡觉"); }
}

public class Cat : Animal
{
    public override void Eat() { Console.WriteLine("吃鱼"); }
}

public class Dog : Animal
{
    public override void Eat() { Console.WriteLine("啃骨头"); }
}

抽象类特点

  1. 抽象类和抽象方法都要用abstract修饰

  2. 抽象类不能实例化

  3. 抽象类可以包含非抽象方法

  4. 子类必须重写所有抽象方法

9.2 接口(interface)

解决C#单继承局限:一个类可以实现多个接口。

public interface IRunnable
{
    void Run();
}

public interface ISwimmable
{
    void Swim();
}

public class Duck : Animal, IRunnable, ISwimmable
{
    public override void Eat() { Console.WriteLine("吃虫子"); }
    public void Run() { Console.WriteLine("摇摇摆摆地跑"); }
    public void Swim() { Console.WriteLine("在水里游"); }
}

接口特点

  • 只能有抽象方法(默认public abstract

  • 不能有字段、构造方法、静态成员

  • 支持多继承(接口可以继承多个接口)

9.3 多态(Polymorphism)

定义:同一类型的引用,调用同一方法时,因指向的具体对象不同而产生不同结果。

多态三大条件

  1. 存在继承关系

  2. 存在方法重写

  3. 父类引用指向子类对象

Animal a1 = new Cat();
Animal a2 = new Dog();
a1.Eat();  // 输出:吃鱼(运行时确定)
a2.Eat();  // 输出:啃骨头(运行时确定)

// 多态的应用:数组存储不同子类
Animal[] animals = { new Cat(), new Dog(), new Duck() };
foreach (Animal a in animals)
{
    a.Eat();  // 不同动物吃不同食物
}

编译看左边,运行看右边

9.4 抽象类 vs 接口

对比项 抽象类 接口
继承/实现数量 单继承 多实现
成员变量 可以有 不能有
成员方法 抽象+非抽象 只能抽象
构造方法 可以有 不能有
适用场景 描述"是什么"(类属) 定义"能做什么"(能力)
  1. 成员变量:编译看父类,运行看父类

  2. 成员方法:编译看父类,运行看子类(动态绑定

  3. 静态方法:编译看父类,运行看父类

学习心得与建议

回顾这两周的学习,有几个感悟想分享给正在路上的朋友们:


  1. 代码是写给人看的:命名要规范(见名知意),注释要说明"为什么"而不是"是什么"。

  2. 先模仿再创造:初期多敲示例代码,理解每行代码的作用,慢慢就能写出自己的东西。

  3. 调试是进步的阶梯:学会使用断点调试,观察变量变化,理解程序执行流程。

  4. 面向对象需要时间消化:类、继承、多态这些概念,光看是学不会的,必须在实践中反复体会。

  5. 保持耐心:编程学习曲线陡峭,遇到bug是常态,解决bug的成就感也是动力源泉。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐