语法:C#语法入门 && C#项目结构解读

968 阅读8分钟

C# 语法入门和项目结构解读

markdown 生成的目录显得我写的东西很多,掘金也没有多级目录

原有基础上删改了一下,没法跳转,凑合看吧

语法入门

注:笔者有 C++JavaPython 等语言的基础,主要目的是混个分,看看就行别太认真。

大概的语法很像 Java,存在 Java 基础学习 C# 只需要半天。

简介

文件后缀为 .cs

  • 面对对象
  • 垃圾回收
  • 有统一类型系统
  • 可以为 null 的类型可防范不引用已分配对象的变量

这句话我也没看明白

.NET体系

C# 是编程语言,.NET 是框架

C# 程序在 .NET 上运行,前者是后者的一部分,不做赘述

C#C++Visual BasicJscriptCOBOL 等都可以在上面运行

C# 的 helloworld
// 调用System命名空间
using System;
// 命名空间声明,包含了类
namespace HelloWorldApplication
{
    // 类声明
    class HelloWorld
    {
        // main 方法,程序的入口
        static void Main(string[] args)
        {
            // 单行注释
            /// 三斜杠注释,符合个人习惯就行
            /* 多行注释
               我的第一个 C# 程序
            */
           
            // 打印 helloworld
            Console.WriteLine("Hello World");
            // pause 暂停,等待继续
            Console.ReadKey();
        }
   }
}

标识符

  • 区分大小写
  • 字母、下划线和 @ 开头
  • 后面可以是字母、下划线、@ 和数字
  • 不能与类名相同
  • 不能是关键字,除非前缀有 @

我也没深究 @ 有什么含义,注解吗难不成

数据类型

值类型引用类型和指针类型

值类型

  • 简单类型
    • 有符号整型:sbyteshortintlong
    • 无符号整型:byteushortuintulong
    • Unicode 字符:char,表示 UTF-16 代码单元
    • IEEE 二进制浮点:floatdouble
    • 高精度十进制浮点数:decimal
    • 布尔值:bool,表示布尔值(truefalse
  • 枚举类型
    • 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,然后赋值。赋非空值。
单问号:使数据类型可空

? 单问号用于对 intdoublebool 等无法直接赋值为 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 */
}

访问修饰符

publicprivateprotected

  • 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#namespacepackage 有点像

区别与联系:

  • 命名空间只是逻辑上的结构,允许物理位置与逻辑结构不一样
  • Java 中类文件的物理结构与逻辑结构必须一致
  • Javapackage 没有子包,package 之间互相并列,没有包含关系
  • C#namespace 允许有自己的 sub-namespace
  • Java 使用 import,而 C# 使用 using namespace

out 关键字

  • 类似 ref,使参数变为引用传递。
  • 区别是,out 在调用函数前强制要求初始化
  • 二者在编译时是一样的,所以无法因为关键字不一样重载

作用:便于通过 out 返回多个值