目录
一、第一天
1、访问修饰符
public>internal>protected>private
- public:公开的,所有都可以访问
- internal:内部的,当前项目内可以访问
- protected:受保护的,只在类的内部及该类的子类可以访问
- private:私有的,只在类的内部可以访问
2、类
类的成员包含字段(静态、非静态)、属性(静态、非静态)、方法(静态、非静态)、常量等
cs
internal class People
{
public string sex;//字段
public int age;//字段
public int Height { get; set; }//属性
public string Name { get; set; }//属性
private int weight;
public int Weight //属性
{
get { return weight; }
set { weight = value; }
}
public void Study() //方法
{
MessageBox.Show("正在学习中...");
}
public static void Meth2() //静态方法
{
}
public readonly string position = "地球"; //只读字段
public static bool alive = true;//静态字段
public const int VALUE = 1; //常量
public static int Value2 { get; set; } //静态属性
}
3、命名规则
字段:Camel(骆驼)命名法:首单词小写,其余每个单词首字母大写
属性、方法:Pascal命名法:每个单词首字母都要大写
常量:每个字母大写
4、变量在内存中的分布
C#中的变量分为两类
1、值类型
值类型变量内存分布在栈上,存储的是值
注意:并不是所有值类型都存储在栈上,当在方法内部定义的局部变量才存在栈上,当作为类的字段存在的值类型,跟着类的实例对象一起存在堆上
cs
People people1 = new People();
people1.Name = "张三";
people1.age = 18;
people1.sex = "男";
这些对象的属性,虽然是值类型,但跟着对象存储在堆上

2、引用类型
引用类型的对象分配上堆上,引用类型变量本身分配在栈上,存储的是对象在堆内存上的地址
cs
People people1 = new People();
people1.Name = "张三";
people1.age = 18;
people1.sex = "男";
People people2 = new People();
people2.Name = "李四";
people2 = people1;
MessageBox.Show(people2.Name);
people2 = people1; 这句代码会把people1存储的堆内存地址复制给people2,效果如下:

此时,people2与people1指向的是堆上的同一块内存,因此people2.Name 是"张三"

二、第二天
1、结构体
结构体是值类型,内部可以有字段、属性、方法等,与类相似但也有区别
cs
// <summary>
/// 结构体是值类型,存储一般都是一些简单的数据类型,当我们想做一些轻量化的运算操作
/// </summary>
struct Teacher
{
int age;
string name;
public int MyProperty { get; set; }
public void Meth()
{
}
}
与类的区别:
(1)类要使用必须通过new关键字实例化,结构体可new也可直接使用

(2)类是引用类型存储在堆上,结构体是值类型存储在栈上
对结构体中的数据赋值就理解成对普通数据类型的赋值操作,都是在栈内存中
(3)类的赋值,是把堆内存地址复制过去,结构体只是把值内容复制过去
cs
Teacher teacher1 = new Teacher();
teacher1.age = 20;
teacher1.name = "李四";
Teacher teacher2;
teacher2 = teacher1;
teacher1.name = "赵六";
MessageBox.Show(teacher2.name);
teacher2 = teacher1 ;这句代码会把teacher1在栈上的值复制给teacher2,然后再改变teacher1在栈上的值,与teacher2无关
(4)类中可以有构造器,结构体中不能有构造器

(5)类可以继承类,结构体不允许继承类但可实现接口
cs
struct Teacher:ISleep
{
public int age;
public string name;
public int MyProperty { get; set; }
public void Meth()
{
}
}
interface ISleep
{
}
2、枚举
与类、结构体、接口等是同一级别
(1)枚举的定义
cs
namespace ClassLearn
{
internal enum MyDirection
{
Left = 2,
Top = 4,
Right = 8,
Bottom = 100
}
}
(2)枚举的使用
cs
internal class People
{
public MyDirection Dir { get; set; }
}
cs
People people1 = new People();
people1.Dir = MyDirection.Left;
MessageBox.Show(people1.Dir.ToString());
int a = (int)people1.Dir;//把枚举类型转换成对应的值
MessageBox.Show(a.ToString());
Type t = typeof(MyDirection);//获取类型方法1
Type t2 = Type.GetType("ClassLearn.MyDirection");//获取类型方法2
Type t3 = people1.Dir.GetType();//获取类型方法3
string s = Enum.GetName(t3, 100);//通过值拿到枚举对应的字符串
MessageBox.Show(s);
MyDirection b = (MyDirection)Enum.Parse(t3, "Left");//通过字符串拿到对应的枚举类型
if(b == MyDirection.Left)
MessageBox.Show("true");
运行结果:
3、常用数据类型转换
4、图片资源的运用
(1)本地导入图片
缺点:图片会嵌入exe文件中, Debug中的.exe文件会随着图片越多,越来越大


当0张图片时:
当1张图片时:
当2张图片时:
(2)后端加载图片

cs
//方式1:绝对路径
pictureBox1.Image = Image.FromFile("D:\\Projects\\Python学习\\CSharpLearn\\ClassLearn\\bin\\Debug\\beautiful.jpg");
//方式2:
string imagePath = AppDomain.CurrentDomain.BaseDirectory + "beautiful.jpg";
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "beautiful.jpg");
pictureBox1.Image = Image.FromFile(path);
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
(3)从资源中加载图片

cs
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox2.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox3.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.Image = Resources.microsoft;
pictureBox2.Image = Resources.登录图标;
pictureBox3.Image = Resources.exit;
运行效果:

已知图片名称,如何在资源文件中提取图片?
cs
ResourceManager resourceManager = new ResourceManager("ClassLearn.Properties.Resources", Assembly.GetExecutingAssembly());
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.Image = (System.Drawing.Image)resourceManager.GetObject("microsoft");
运行效果:

5、扩展方法
既然类里面已经可以定义方法了,为什么还要多此一举发明"扩展方法(Extension Methods")?
本质上,这是为了在不修改原有代码 的前提下,实现功能的无限延伸。
我们可以从以下几个核心场景来理解它的必要性:
1. 拯救"无法修改"的类(最核心原因)
在开发中,你经常会遇到以下情况:
- 系统内置类 :比如
string、int、DateTime、List<T>。这些类是微软写在 .NET 底层框架里的,你根本没有权限去修改它们的源码。 - 第三方库:你引用的别人写的 DLL 文件,同样无法修改。
如果你突然觉得 string 类缺少一个"判断是否为手机号"的方法,你没法去 string 类里加代码。这时候,扩展方法就成了救星,它允许你像调用原生方法一样,给这些"封闭"的类增加自定义功能。
2. 保持代码的"链式调用"与可读性
假设你要给 string 加一个"去除所有空格"的功能。
- 不用扩展方法 :你只能写一个静态工具类
StringHelper.RemoveSpaces(myString)。当多个操作连在一起时,代码会变成这样,非常难读:
StringHelper.Trim(StringHelper.RemoveSpaces(StringHelper.ToLower(myString))) - 使用扩展方法 :你可以像这样优雅地链式调用:
myString.ToLower().RemoveSpaces().Trim()
这种写法不仅符合人类的阅读习惯,而且极大地提升了代码的整洁度。
3. 遵循"开闭原则"(OCP)
面向对象设计有一个重要原则:对扩展开放,对修改封闭 。
如果一个类已经写好了,并且有很多地方都在用它,你直接去修改这个类的源码(加个方法),可能会不小心引入 Bug,影响所有调用它的地方。扩展方法允许你在外部安全地增加功能,完全不用动原来的类。
4. 为"接口"提供默认实现
在 C# 8.0 之前,接口(Interface)里是不能写方法体的,只能定义签名。如果你想给所有实现了 IEnumerable 接口的集合(如 List, Array)统一增加一个功能,你没法在接口里写代码。
通过扩展方法,你可以针对"接口"进行扩展。只要一个类实现了这个接口,它就自动拥有了这个扩展方法。LINQ 就是利用这个机制,给所有的集合加上了 Where, Select, OrderBy 等海量功能。
核心语法规则:
- 必须写在**静态类(static class)**中。
- 方法必须是静态方法(static) ,且第一个参数必须加上
this关键字(代表你要扩展的那个对象)。
原有People类
cs
internal class People
{
public MyDirection Dir { get; set; }
}
扩展类
cs
//1、必须是静态类
internal static class PeopleExtension
{
//2、必须是静态方法,第一个参数带this
public static void Study(this People people)
{
MessageBox.Show("People在学习...");
}
}
扩展方法的使用
虽然扩展类里面写的是静态方法,但实际上扩展的是类的对象的方法
因为扩展方法的第一个参数加了 this ,这就意味着这个方法必须绑定到一个具体的对象实例上才能调用
而类的静态方法不能扩展,只能写静态工具类来增加静态方法
cs
People people = new People();
people.Study();
运行效果:

当然,一个扩展类里可以为多种类做扩展方法
三、第三天
1、var、object、dynamic区别
(1)object
定义的数据类型可以改变
cs
//object定义的数据类型可以改变
object f = new People();
f = 1;
f = "Hello World";
f = 2.5f;
但缺点是涉及到装箱、拆箱,性能下降
装箱: 将值类型(隐式)转换成引用类型,会在托管堆上为对象分配内存
**拆箱:**将引用类型(显式)强制转换成值类型
cs
//装箱:将值类型(隐式)转换成引用类型,会在托管堆上为对象分配内存
int val = 100;
object f = val;
//拆箱:将引用类型(显式)强制转换成值类型
int b = (int)f;
(2)var关键字
只能放在方法里作为局部变量使用


var定义好的数据类型不能改变,var定义的变量是在编译环境下检测数据类型


var方便编译器自动检测数据类型,不需要我们考虑
cs
List<People> list = new List<People>();
var g = list.Where(x=>x.Age == 100).ToList().FirstOrDefault();
var p = list.Where(x => x.Age == 100).ToArray();
(3)dynamic
dynamic是程序运行过程中才识别数据实际类型


dynamic定义的变量在运行过程中可以改变数据类型

|---------|-----------|----------------|
| 关键字 | 能否定义成全局变量 | 定义好的变量能否改变数据类型 |
| object | √ | √ |
| var | × | × |
| dynamic | √ | √ |
[object/var/dynamic的区别]





