C# 零基础入门学习笔记:从Hello World到面向对象
前言
作为一名编程新手,我选择了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后面必须有break,default可以放在任何位置但建议放最后。
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 构造方法(构造函数)
作用:初始化对象,在创建对象时为属性赋初始值。
特点:
-
方法名与类名完全相同
-
没有返回值,连
void也不能写 -
创建对象时自动调用
-
可以重载(多个参数不同的构造方法)
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关键字
-
区分成员变量和局部变量:
this.name = name; -
在构造方法中调用其他构造方法:
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 子类 : 父类 { }
继承的特性:
-
单根性:一个类只能有一个直接父类
-
传递性:孙子类继承父类的父类的所有非私有成员
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("啃骨头"); }
}
抽象类特点:
-
抽象类和抽象方法都要用
abstract修饰 -
抽象类不能实例化
-
抽象类可以包含非抽象方法
-
子类必须重写所有抽象方法
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)
定义:同一类型的引用,调用同一方法时,因指向的具体对象不同而产生不同结果。
多态三大条件:
-
存在继承关系
-
存在方法重写
-
父类引用指向子类对象
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 接口
| 对比项 | 抽象类 | 接口 |
|---|---|---|
| 继承/实现数量 | 单继承 | 多实现 |
| 成员变量 | 可以有 | 不能有 |
| 成员方法 | 抽象+非抽象 | 只能抽象 |
| 构造方法 | 可以有 | 不能有 |
| 适用场景 | 描述"是什么"(类属) | 定义"能做什么"(能力) |
-
成员变量:编译看父类,运行看父类
-
成员方法:编译看父类,运行看子类(动态绑定)
-
静态方法:编译看父类,运行看父类
学习心得与建议
回顾这两周的学习,有几个感悟想分享给正在路上的朋友们:
-
代码是写给人看的:命名要规范(见名知意),注释要说明"为什么"而不是"是什么"。
-
先模仿再创造:初期多敲示例代码,理解每行代码的作用,慢慢就能写出自己的东西。
-
调试是进步的阶梯:学会使用断点调试,观察变量变化,理解程序执行流程。
-
面向对象需要时间消化:类、继承、多态这些概念,光看是学不会的,必须在实践中反复体会。
-
保持耐心:编程学习曲线陡峭,遇到bug是常态,解决bug的成就感也是动力源泉。
更多推荐


所有评论(0)