目前发现Solidworks二次开发中C#提供的API和环境更为合适,所以将我写的C++插件转换为了C#,由于本人有C++和Java的语法基础,所以本笔记不适用于初学者,学习路线如下:
1.C#学习
学习一门语言,先要学习他的代码规范,这是我让AI给我找的C#的代码规范,后续再补充:
C#
- 类名、方法名、属性、接口、命名空间、枚举使用大驼峰(如User)
- 方法名、变量名使用小驼峰(如getName())
- 接口以I开头
- 异步方法以 Async结尾
- 布尔属性用 Is/ Can/ Has开头
- 私有实例字段(_camelCase(下划线 + 小驼峰))
- 私有静态字段(s_camelCase)
- 线程静态字段(t_camelCase)
- 常量(PascalCase),这点和Java还是不一样的
1.1第一个Hello World
其中有两个知识点需要知道:
- namespace:命名空间是一个声明性区域,用于组织相关类型,并避免名称冲突。简单来说,就是将类、接口、枚举等类型包装在一个作用域中,和C++的定义相似
- using:允许你使用命名空间中定义的类型,而无需写出其完全限定名。也就是如果你定义了using,就不需要将类型的完整路径写出来了,但是引入的多个命名空间中有同一个类型,使用该类型会编译报错,如程序2所示:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World");
// 这段话的意思是让
Console.ReadLine();
}
}
}
程序2
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using n1;
using n2;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
A a;
}
}
}
namespace n1
{
class A
{
}
}
namespace n2
{
class A
{
}
}
1.2注释
C#
// 单行注释
/*
*
*/
/// 三个///快捷注释
/// <summary>
/// 摘要注释
/// </summary>
/// <param name="args">参数注释</param>
static void Main(string[] args)
{
}
1.3变量
C#
int age = 20;
string name = "Tom";
double salary = 5000;
1.3.1var变量
var是类型推荐的语法糖,变量在编译期就已经确定了类型,之后永远不能改变。var关键字指示编译器根据初始化表达式右侧的结果,推断出变量的类型。
C#
var age = 20;
1.4常量
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
const string PI = "3.1415";
Console.WriteLine(PI);
Console.ReadLine();
}
}
}
1.4.1const
- const 是编译期常量
- const 在编译后被"内联"成值本身
- const 永远属于类型,不属于对象
1.5数据类型
1.5.1分类
text
C# 数据类型
├── 值类型 (Value Types)
│ ├── 简单类型
│ │ ├── 整型(int, long, short, byte...)
│ │ ├── 浮点型(float, double)
│ │ ├── decimal
│ │ ├── bool
│ │ └── char
│ ├── 枚举 enum
│ └── 结构体 struct
│
├── 引用类型 (Reference Types)
│ ├── class
│ ├── interface
│ ├── delegate
│ ├── record
│ ├── string
│ ├── array
│ └── object
│
└── 特殊类型
└── dynamic
1.5.2值类型
- 变量直接存储数据本身
- 存放在栈上(大多数情况)
- 赋值时会复制整个值
- 默认按值传递
1.5.2.1整数
| 类型 | 大小 | 范围 |
|---|---|---|
| sbyte | 8 bit | -128 ~ 127 |
| byte | 8 bit | 0 ~ 255 |
| short | 16 bit | -32768 ~ 32767 |
| ushort | 16 bit | 0 ~ 65535 |
| int | 32 bit | ±21亿 |
| uint | 32 bit | 0 ~ 42亿 |
| long | 64 bit | ±9×10¹⁸ |
| ulong | 64 bit | 0 ~ 1.8×10¹⁹ |
1.5.2.2浮点数
| 类型 | 精度 | 说明 |
|---|---|---|
| float | 7位 | 单精度 |
| double | 15~16位 | 双精度(默认) |
| decimal | 28~29位 | 金融/货币计算 |
1.5.2.3布尔与字符
C#
bool isOk = true;
char c = 'A'; // 单引号,16 位 Unicode
1.5.2.4枚举
C#
enum Status
{
Pending,
Approved,
Rejected
}
Status s = Status.Approved;
1.5.2.5结构体
C#
struct Point
{
public int X;
public int Y;
}
1.5.3引用类型
- 变量存储的是 对象的地址(引用)
- 对象存放在 堆(Heap)
- 赋值时只复制引用(两个变量指向同一个对象)
- 默认按引用传递
1.5.3.1类
C#
class Person
{
public string Name;
public int Age;
}
Person p1 = new Person();
p1.Name = "Tom";
Person p2 = p1; // 复制引用
p2.Name = "Jack";
Console.WriteLine(p1.Name); // Jack
1.5.3.2string
C#
string s1 = "Hello";
string s2 = s1;
s2 = "World";
Console.WriteLine(s1); // Hello
1.5.3.3数组
C#
int[] arr = new int[3];
arr[0] = 10;
1.5.3.4object
C#
object o1 = 123;
object o2 = "abc";
1.5.3.4.1装箱和拆箱
我看到的以为一惊,这不是Java中的基本数据类型和包装类的转换吗。但是不对呀,在C#中,没有包装类呀,object不是能包含所有嘛?所以我就迷茫了,然后查了查AI,AI告诉我,在 C# 中,装箱(Boxing)和拆箱(Unboxing) 指的是 值类型(Value Type)与 object(引用类型)之间的转换机制。
- 装箱:把值类型包装成一个托管堆上的对象
c#
int i = 10;
object o = i; // 装箱
- 拆箱:把被装箱的对象还原为值类型
c#
object o = 10;
int i = (int)o; // 拆箱
1.5.4数据类型转换
1.5.4.1隐式转换
不会丢失数据。
c#
int a = 10;
double b = a;
1.5.4.2显式转换
c#
double a = 3.14;
// 小数会被截断
int b = (int)a;
1.5.4.3Convert和Parse
c#
string age = "18";
int n = Convert.ToInt32(age);
int n = int.Parse("100");
1.5.4.4TryParse
c#
bool ok = int.TryParse("100", out int n);
1.6运算符
和Java和C++相似,我就不学了,其中比较生僻的就是??=,特别介绍一下,??=,当变量为 null时,才给它赋值;不为 null就什么都不做。
1.7字符串
C# 里的 string是一个非常"特殊"的类型:语法上像值类型,本质上却是引用类型。
1.7.1三大特性
- 不可变性:字符串一旦创建,就不能修改(这点和Java相似,Java中字符串也是不可变的,不管是拼接还是substring,本质都是创建一个字符串对象)
- 字符串驻留:为了节省内存,CLR 会维护一个 字符串常量池(擦,跟Java一摸一样,也是创建维护一个字符串常量池)
- 重载了
==运算符:虽然它是引用类型,但==比较的是 内容,不是引用
1.7.2注意点
- 1.大量拼接使用StringBuilder(和Java一样,也是线程不安全的)
1.7.3常见的方法
C#
s.Contains("abc");
s.StartsWith("a");
s.EndsWith("z");
s.IndexOf("a");
s.Substring(0, 5);
s.Split(',');
1.7.4格式化
C#
string.Format("Name: {0}, Age: {1}", name, age);
$"Name: {name}, Age: {age}" // 插值字符串(推荐)
我写的例子如下:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Person
{
public string name;
public int age;
}
class Program
{
static void Main(string[] args)
{
Person yancey = new Person();
yancey.age = 26;
yancey.name = "张三";
// 格式化
string s = $"此人的名字是{yancey.name},此人的年纪是{yancey.age}";
Console.WriteLine(s);
Console.ReadLine();
}
}
}
1.7.5@ 字符串 和 $ 字符串
1.7.5.1@字符串(逐字字符串)
取消字符串中的转义字符处理,让反斜杠 ``被当作普通字符,而不是转义符。并且@字符串可以直接换行
c#
string path = @"C:\Users\Admin\Desktop";
1.7.5.2$字符串(插值字符串)
在字符串中直接嵌入表达式(变量、方法调用等),比 string.Format更直观。(string.Format,我靠,真和Java一摸一样),也可以和@字符串组合使用
c#
string name = "张三";
int age = 25;
string msg = $"姓名:{name},年龄:{age}";
1.8控制台的输入和输出
C#
Console.WriteLine("Hello");
string name = Console.ReadLine();
1.9逻辑控制
1.9.1if-else
c#
if(age>18)
{
}
else
{
}
1.9.2三元表达式
c#
string result = age > 18 ? "成年" : "未成年";
1.9.3switch
C#
// 老版(和java一样,用这个就行)
switch(type)
{
case 1:
break;
case 2:
break;
}
// 新版
string result = age switch
{
>18 => "成年",
_ => "未成年"
};
1.10循环
1.10.1for
c#
for(int i=0;i<10;i++)
{
}
1.10.2foreach
c#
foreach(var item in list)
{
}
1.10.3while
c#
foreach(var item in list)
{
}
1.10.4do while
c#
do
{
}while(false);
1.11break continue return
c#
- break:退出循环
- continue:跳过本次循环。
- return:结束方法
1.12数组
c#
// 一维
int[] nums = {1,2,3};
// 二维
int[,] nums =
{
{1,2},
{3,4}
};
// 数组的访问
nums[0];
1.13枚举
c#
enum Color
{
Red,
Green,
Blue
}
1.14Nullable
Nullable<T>是一个泛型结构体,用来让"值类型"可以表示 null。常见的写法如下
c#
int? age = null;
// 等价于
Nullable<int>
1.15nameof和default
nameof用于在编译期获取变量、类型、成员的名字(字符串),但不会求值。default使用了获取某种类型的默认值。
c#
string name = nameof(User.Name);
// 结果是:"Name"
default(int)
// 结果是0
1.16类
我感觉和Java相似,类就是现实世界对象的抽象,一个类通常包括:字段、属性、方法、构造方法、事件
字段、方法、构造方法、this、static、多态都和Java一模一样,但是属性这个东西是Java中没有的,所以我重点记录一下。
1.16.1属性
属性(Property)是 C# 区别于 Java 最关键的语言特性之一,而且它表面上很简单,底下却藏着不少设计哲学。属性是一种用于访问对象状态的成员,它提供灵活的机制来读取、写入或计算私有字段的值。实际我看下来,这玩意就是get方法和set方法的集合,不过还是挺有意思的。这玩意真就是封装的完全体现。
c#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Person
{
// 定义两个字段
private string _name;
private int _age;
// 声明属性
public string Name
{
get;set;
}
public int Age
{
get
{
return _age;
}
set
{
this._age = value;
}
}
// 定义方法
public string toString()
{
return $"我的名字叫做{this._name},我的年龄是{this._age}";
}
// 构造方法
public Person()
{
}
public Person(string name, int age)
{
this._age = age;
this._name = name;
}
}
class Program
{
static void Main(string[] args)
{
Person yancey = new Person("张三", 16);
Console.WriteLine(yancey.toString());
Console.ReadLine();
}
}
}
1.16.2继承
C#的继承和C++还是比较像的,但是C#中没有继承权限控制。同时和Java也是不一样的,Java调用父类是使用的super关键字,C#使用的是base关键字
c#
public class Dog:Animal
{
public void Test()
{
base.Eat();
}
}
1.16.3virtual、override、abstract
- virtual:父类说"我可以被重写"
- override:子类说"我就是要重写它"
- abstract:父类说"我根本不实现,你们必须实现"
c#
using System;
namespace Demo
{
// 抽象类:动物(不完全的类)
abstract class Animal
{
// abstract:父类不实现,子类必须实现
public abstract void Speak();
// virtual:父类有默认实现,子类可选重写
public virtual void Sleep()
{
Console.WriteLine("动物在睡觉");
}
}
// Dog 继承 Animal
class Dog : Animal
{
// override:重写抽象方法(必须)
public override void Speak()
{
Console.WriteLine("狗叫:汪汪");
}
// override:重写虚方法(可选)
public override void Sleep()
{
Console.WriteLine("狗趴着睡觉");
}
}
// Husky 继承 Dog
class Husky : Dog
{
// override:继续重写虚方法
public override void Sleep()
{
Console.WriteLine("哈士奇拆家式睡觉");
}
}
class Program
{
static void Main(string[] args)
{
Animal a1 = new Dog();
Animal a2 = new Husky();
a1.Speak(); // 狗叫:汪汪
a1.Sleep(); // 狗趴着睡觉
Console.WriteLine("-----");
a2.Speak(); // 狗叫:汪汪
a2.Sleep(); // 哈士奇拆家式睡觉
}
}
}
1.16.4interface
就是Java中的接口,但是不一样的是Java中类实现接口是通过implement关键字,而C#是通过
:,竟然和集成一样,汗颜!
c#
public interface IOpenable
{
void Open();
}
public class Part : IOpenable
{
public void Open()
{
}
}
1.16.4sealed
就是禁止继承,但是可以实现接口
c#
public sealed class Logger
{
}
1.16.5readonly
只能构造函数赋值
c#
private readonly string _path;
1.16.6partial
把一个类 / 结构体 / 接口 / 方法"拆成多份文件写",编译器在编译时帮你合成一个完整的类型。
编译后等价于
1.16.6record
record就是 "为'存数据'而生的类",默认帮你做好 相等比较、ToString、不可变性,写起来极省代码。本质上就是一个类,但是由record创建的类不能修改,所以我的理解,这玩意就是天生用来创建配置类的(没有自定义方法的)
c#
public record Person(string Name, int Age);


