C# 语法入门和项目结构解读
markdown 生成的目录显得我写的东西很多,掘金也没有多级目录
原有基础上删改了一下,没法跳转,凑合看吧
语法入门
注:笔者有 C++
、Java
、Python
等语言的基础,主要目的是混个分,看看就行别太认真。
大概的语法很像 Java
,存在 Java
基础学习 C#
只需要半天。
简介
文件后缀为 .cs
- 面对对象
- 垃圾回收
- 有统一类型系统
- 可以为
null
的类型可防范不引用已分配对象的变量
这句话我也没看明白
.NET体系
C#
是编程语言,.NET
是框架
C#
程序在 .NET
上运行,前者是后者的一部分,不做赘述
C#
、C++
、Visual Basic
、Jscript
、COBOL
等都可以在上面运行
C# 的 helloworld
// 调用System命名空间
using System;
// 命名空间声明,包含了类
namespace HelloWorldApplication
{
// 类声明
class HelloWorld
{
// main 方法,程序的入口
static void Main(string[] args)
{
// 单行注释
/// 三斜杠注释,符合个人习惯就行
/* 多行注释
我的第一个 C# 程序
*/
// 打印 helloworld
Console.WriteLine("Hello World");
// pause 暂停,等待继续
Console.ReadKey();
}
}
}
标识符
- 区分大小写
- 字母、下划线和 @ 开头
- 后面可以是字母、下划线、@ 和数字
- 不能与类名相同
- 不能是关键字,除非前缀有 @
我也没深究 @ 有什么含义,注解吗难不成
数据类型
值类型、引用类型和指针类型
值类型
- 简单类型
- 有符号整型:
sbyte
、short
、int
、long
- 无符号整型:
byte
、ushort
、uint
、ulong
- Unicode 字符:
char
,表示 UTF-16 代码单元 - IEEE 二进制浮点:
float
、double
- 高精度十进制浮点数:decimal
- 布尔值:
bool
,表示布尔值(true
或false
)
- 有符号整型:
- 枚举类型
enum E {...}
格式的用户定义类型。enum
类型是一种包含已命名常量的独特类型。 每个enum
类型都有一个基础类型(必须是八种整型类型之一)。enum
类型的值集与基础类型的值集相同。
- 结构类型
- 格式为
struct S {...}
的用户定义类型
- 格式为
- 可以为
null
的值类型- 值为
null
的其他所有值类型的扩展
- 值为
- 元组值类型
- 格式为
(T1, T2, ...)
的用户定义类型
- 格式为
引用类型
- 类类型
- 其他所有类型的最终基类:
object
- Unicode 字符串:
string
,表示 UTF-16 代码单元序列 - 格式为
class C {...}
的用户定义类型
- 其他所有类型的最终基类:
- 接口类型
- 格式为
interface I {...}
的用户定义类型
- 格式为
- 数组类型
- 一维、多维和交错。 例如:
int[]
、int[,]
和int[][]
- 一维、多维和交错。 例如:
- 委托类型
- 格式为
delegate int D(...)
的用户定义类型
- 格式为
类型声明:六种
类类型、结构类型、接口类型、枚举类型、委托类型和元组值类型。
- Class 类
- Struct 结构
- Interface 接口
- Delegate 委托
类似于函数的指针
以上类型全部支持泛型,可用其他类型参数化
动态(Dynamic)类型
- 与对象类型相似
- 对象类型在编译时检查
- 动态类型在运行时检查类型
类似于脚本语言的
var
吧
字符串类型
注意:
虽然 C#
区分大小写,但是 string
关键字是 String
的别名,正常使用时可以看作是等价的。
在 C#
中,string
的使用优先级似乎比 String
要高,我也不知道为啥,不太习惯。
字符串(String)类型的值可以通过两种形式进行分配:引号和 @引号。
逐字字符串:可以将转义字符(\)当作普通字符,相当于(\\)
@ 之后就不用转义了吧,方便cv,看起来也顺眼一点
string str = @"C:\Windows";
等价于
string str = "C:\\Windows";
@ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。
string str = @"<script type=""text/javascript"">
<!--
-->
</script>";
指针类型
与C和C++相同,万事不觉星号批(*p
)
可空数据
nullable 类型
可空类型,在基础值类型的范围,加上 null
值
Nullable<Int32> // 可空的 Int32 类型;
Nullable<bool> // true、false 和 null
单问号?和双问号??
大概解读:
- 单问号赋予数据类型可以为
null
- 双问号判断是否为
null
,然后赋值。赋非空值。
单问号:使数据类型可空
?
单问号用于对 int
、double
、bool
等无法直接赋值为 null
的数据类型进行 null
的赋值
int? i = 3;
Nullable<int> i = new Nullable<int>(3);
二者等价
int i;// 默认值:0
int? ii;// 默认值:null
双问号:合并运算符(??)
??
双问号用于判断一个变量在为 null
的时候返回一个指定的值。
- 若第一个为
null
, 则返回第二个数值 - 若第一个不为
null
,则返回第一个数值
double? num1 = null;
double? num2 = 3.14;
double num3;
num3 = num1 ?? 5.34; // num1 为空,返回5.34
num3 = num2 ?? 5.34; // 非空,,返回3.14
简单的输入与输出
输入值
System
命名空间中的 Console
类提供了一个函数 ReadLine()
int num;
// 读取,接受,并转类型为32位int
num = Convert.ToInt32(Console.ReadLine());
输出值
输出调试
Console.Write();
/// cout<<"";
/// System.out.print();
/// print();
Console.WriteLine();
/// cout<<endl;
/// System.out.println();
函数与方法
值传递和引用传递
值传递,不影响原数据:
public void swap(int x, int y){
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
}
引用传递,ref
(Reference)关键字关联参考
public void swap(ref int x, ref int y){
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
}
访问修饰符
public
、private
、protected
public
公开private
私有,子类也无法访问protected
保护,子类可以继承调用protected internal
:引用程序集内公开(package public?)
默认是 internal
Internal 内部
internal
只能在命名空间内(项目内)被访问到。public
修饰的是可以在命名空间外(项目外)访问到的。
数组
- 任意类型一维数组和多维数组
- 无需声明直接使用
int[]
一维数组int[,]
二维数组int[][]
由int
类型的一维数组或“交错”数组构成的一维数组
C# 数组细节
- 多维数组
- 交错数组,数组的数组
- 传递数组给函数,传递指针
- 参数数组,传参
- Array 类。System命名空间中定义,数组的基类。
数组的通解
经典千篇一律
声明数组
data[] arrayName;
初始化数组
声明数组不会初始化。
数组是引用类型,需要用 new
关键字创建实例。
double[] balance = new double[10];
赋值
存在隐式初始化,例如 int
数组默认值为 0
单个元素赋值
balance[0] = 4500.0;
声明数组时赋值
double[] balance = { 2340.0, 4523.69, 3421.0};
复制数组,此时引用地址相同,指针地址相同。
int[] marks = new int[] {xx,xxx};
int[] score = marks;
访问数组元素
- 通过下标单个访问
- foreach 遍历循环
交错数组
交错数组是数组的数组,可以包含多维数组
- 交错数组的元素是数组
- 各元素之前长度可以不同,可以是一维数组,也可以是二维数组。
声明数组,具有3个元素,每个元素都是一个数组
int[][] jaggedArray = new int[3][];
对每个元素数组开始初始化
jaggedArray[0] = new int[5];
jaggedArray[1] = new int[4];
jaggedArray[2] = new int[2];
此时,每个元素都是一个一维数组,但是长度各不相同
赋值初始化:
jaggedArray[0] = new int[] { 1, 3, 5, 7, 9 };
jaggedArray[1] = new int[] { 0, 2, 4, 6 };
jaggedArray[2] = new int[] { 11, 22 };
也可以在交错数组声明时初始化
int[][] jaggedArray2 = new int[][]{
new int[] { 1, 3, 5, 7, 9 },
new int[] { 0, 2, 4, 6 },
new int[] { 11, 22 }
};
总结:数组的数组,与多维数组最大的不同是对每个一维/多维数组的长度没有要求。
结构体(Struct)
struct Books{
public string title;
public int book_id;
};
结构体与类
C# 中,二者默认访问权限均为 private
- 前者是值类型,在栈上分配内存
- 后者是引用类型,在堆上分配内存
- 结构不能继承
- 结构不能声明默认构造函数
- 结构中不能赋予默认初始值。
静态构造函数:C#
的结构可以定义构造函数
- 结构可带有方法、字段、索引、属性、运算符方法和事件。
- 可以实现一个或多个接口
- 使用
New
创建实例,会调用构造函数 - 与类不同,结构可以不使用
New
即可实例化。 - 此时(不使用
new
),所有字段初始化之后,字段才被赋值,对象才被使用。
再多的没研究
基于VS的C#项目的结构
概括
解决方案:.sln
项目入口
项目文件:.csproj
命名空间和类:.cs
Properties 文件夹:
项目版本信息定义,编译方式定义等
bin 文件夹:
项目发布,生成文件夹
Debug 文件夹:
调试版本输出,含有断电调试信息等
Release 文件夹:
运行版本输出
obj 文件夹:
vs开发编译缓存
注:Properties、bin、obj,这几个文件夹不需要程序员管理
*.config 文件:
Asp.net 配置文件
appsettings.json 文件:
.Net Core 配置文件定义
文件管理器目录
项目
APP1
|-- Properties
| |-- AssemblyInfo.cs 存储当前项目的程序集信息
| |--Program.cs 源代码文件
本地目录结构:/xxx/source/repos/ConsoleApp1/
|-- ConsoleApp1 项目文件夹
| |-- bin [编译]保存项目生成后的程序集,存在Debug 和 Release 两个版本。默认输出路径。
| | |-- Debug
| | | |-- .exe 或 .dll
| | | |-- .pdb 记录断点、调试信息
| | | |-- .XML
| | |-- Release
| | | |-- .exe 或 .dll
| | | |-- .pdb
| |-- obj [编译]保存每个模块的编译结果
| |-- Properties
| | |-- AssemblyInfo.cs 存储当前项目的程序集信息
| |-- App.config
| |-- ConsoleApp1.csproj 项目的配置文件
| |-- Program.cs 核心文件,源代码
|-- ConsoleApp1.sln 解决方案文件,vs打开的入口
一般一直或打包程序,只需要 Debug
文件即可。
包含
.exe
文件、.dll
文件、.ini
文件、.xml
文件、.pdb
文件、.lib
文件、.config
、.manifest
文件、.png
文件、.gif
文件、.cur
文件、自定义文件夹等。在
\bin\release\
目录下只有一个.exe
或.dll
文件,Release
模式下不包含调试信息,并对代码进行了优化。
可执行文件:bin\Debug\
ConsoleApp1.exe:
可执行文件,双击可以直接运行。
ConsoleApp1.exe.config:
应用程序的配置文件,上面介绍的App.config文件的克隆。
ConsoleApp1.pdb:
.pdb(Program Database File,程序数据库文件),VS编译链接时生成的文件。
DPB文件主要存储了 VS 调试程序时所需要的基本信息,主要包括源文件名、变量名、函数名、FPO(帧指针)、对应的行号等等。
因为存储的是调试信息,所以一般情况下PDB文件是在Debug模式下才会生成。
鬼才发现
namespace 命名空间
C#
的 namespace
跟 package
有点像
区别与联系:
- 命名空间只是逻辑上的结构,允许物理位置与逻辑结构不一样
Java
中类文件的物理结构与逻辑结构必须一致Java
的package
没有子包,package
之间互相并列,没有包含关系C#
的namespace
允许有自己的sub-namespace
Java
使用import
,而C#
使用using namespace
out 关键字
- 类似
ref
,使参数变为引用传递。 - 区别是,
out
在调用函数前强制要求初始化 - 二者在编译时是一样的,所以无法因为关键字不一样重载
作用:便于通过 out
返回多个值