• C#.Net筑基-基础知识


    image.png


    01、C#基础概念

    1.1、C#简介

    C# (读作C Sharp)是由微软公司开发的一种面向对象、类型安全、高效且简单的编程语言,最初于 2000 年发布,并随后成为 .NET 框架的一部分。所以学习C#语言的同时,也是需要同步学习.NET框架的,不过要要注意C#与.NET的对应版本。

    image.png

    C#语言和Java类似,是一门简单易用、应用广泛的高级编程语言。结合了面向对象编程、事件驱动、泛型编程、异步编程等众多现代化编程概念,属于编译性语言。主要特点:

    • 面向对象:封装(类与对象)、继承(类继承、接口继承)、多态等(类继承、多接口继承实现)。
    • 类型安全:强类型安全,在编译时检测,提高代码可靠性。
    • 交互性,易于各种语言交互,如VB、F#、C++、JavaScript、Python等。
    • GC管理:自动内存管理,C# 采用垃圾回收机制,无需申请、释放内存,减少内存泄漏风险。
    • 开源跨平台:.NETCore框架是开源跨平台的,支持多种操作系统。
    • 强大的标准库,C#拥有丰富的标准类库(.NET Framework或.NET Core),内置各种功能和工具。
    • 宇宙第一开发IDE: Visual Studio 提供了强大的开发、调试和设计工具。

    image.png

    .NET Framework最高支持C#语法版本是C#7.3.NET Standard 2.1,可以基于该版本学习,后面的版本可以根据需要学习新增特性即可。

    image.png

    图来源:C#.NET体系图文概述

    1.2、开发环境

    📢 推荐安装Enterprise 企业版!功能最全。开发工具了解:《Visual Studio工具使用入门

    image.png

    1.3、Hello World

    using System; //引用using
    namespace ConsoleApp_Net48  //申明命名空间
    {
        internal class Program  //定义类
        {
            static void Main(string[] args)   //方法,控制台入口函数
            {
                Console.WriteLine("Hello World!");  //控制台打印输出
                Console.ReadLine();
            }
        }
    }
    
    • using 引用命名空间资源。
    • namespace 命名空间 :一组代码资源(类、结构、枚举、委托等)的集合。
    • class 类:定义一个类,C#中最常用的代码组织单元。
    • 方法:特定功能的代码块,有输入和输出(也可为空)。

    02、基础语法

    C#代码以行为单位,(半角)分号;结尾,花括号{ 代码块 }为一个独立的代码区域。

    image

    2.1、变量申明

    变量类型 变量名 = 值,变量就是对象值的名字,就像人的名字一样,通过变量来访问具体的对象值。变量可以是局部变量、参数、字段、数组、对象实例、委托等。

    • 申明变量、赋值可以一次性,也可分开,也可以一次性申明多个变量。
    • 变量的使用前必须初始化(赋值),使用未赋值的变量会引发异常。
    • 同一作用域内,一个变量名只能申明一次,不可重复。
    • 字符串用“双引号”,单个字符用'单引号'

    也可以用var申明,编译器通过值类型推断其具体变量类型,因此申明时必须赋值,var是一个语法糖。

    int age; //先申明,后赋值
    age = 12;
    float weight = 55.55f;
    double height = 188.88d; //末尾可以不用带d,默认就是double
    var name = "sam";
    var lastName = 'T';
    string f1, f2, f3 = "F3"; //申明了3个变量,对f3赋值了
    var user = new User();    //创建一个User对象实例
    User user2 = new User();  //创建一个User对象实例
    

    2.2、代码风格

    C#代码的命名风格大多为驼峰命名为主,相对比较统一,不像前端那么麻烦,HTML、CSS、JS、URL各不相同。

    • 区分大小写,字母、数字、下划线组成,不能数字开头,不能是关键字。C#中的关键字还是挺多的,参考 C# 关键字
    • 驼峰命名
      • 文件名、类名、接口、方法等都是大驼峰:UserName
      • 局部变量为小驼峰:userName
      • 字段:下划线+小驼峰/大驼峰都可以 _userName_UserName,或者"m_"开头,按照团队规范即可。
      • 常量:全大写(下划线分割),或者大驼峰都可以,USER_NAMEUserName
    public string UserName { get => _UserName; set => UserName = value; }
    
    public string _UserName;
    
    public const int Max=100;
    
    public static int MaxAge =100;
    
    private static int _MinAge = 20;
    
    public void Sum(int a, int b)
    {
    	int sum = a + b;
    }
    

    2.3、注释://

    • 单行注释//开头。
    • 多行注释/*多行注释 */(同css)
    • XML注释///用于类型定义、方法、属性、字段等成员的XML注释,参考:《C#文档XML注释
    /// 
    /// XML注释,计算和
    /// 
    public void Sum(int a, int b)
    {
    	//单行注释
    	int sum = a + b;
    	/*
    	多行注释
    	输出结果
    	*/
    	Console.WriteLine(sum);
    }
    

    2.4、作用域

    变量的作用域就是指变量的有效范围,C#中的作用域可以简单理解为 花括号{ 代码块 } 的范围,可以是类、方法、控制逻辑(for、while等),或者就一个单纯的{}

    • 一个花括号 {}内代码为一个独立的代码区域,有独立的作用域,变量在该作用域内有效。
    • 花括号 {}作用域可以多级嵌套,比如类中包含方法,方法内包括控制逻辑,子作用域可以访问父级的变量(字段、属性、方法、具备变量)。简单理解就是:子级可以访问父级的成员
    private int x = 1; //类字段
    void Main()
    { 
    	var y = 1 + x; //私有变量
    	if (y > 0)
    	{
    		int z = x + y + 1; //可以访问父级成员
    		Console.WriteLine(z);
    		{
    			int w = x+y+z+1;  //可以访问父级成员,及父级的父级
    			Console.WriteLine(w);
    		}
    	}
    }
    

    📢一般情况下,变量的作用域是由代码的词法环境(就是编写代码的位置)来决定的,这比较容易理解。例外情况就是C#中的闭包,常见于动态函数、委托。


    03、申明语句

    申明变量 说明
    Type v 申明指定类型的变量,int xList list
    var 隐式匿名类型var,用var申明变量,编译器根据值推断出类型变量,因此要求必须赋初始值。
    const 申明一个常量,申明时必须赋初始值,且不可修改
    ref reference 变量是引用另一个变量(称为引用)的变量,可以看做是其别名(分身)
    void Main()
    {
    	int x =100;
    	List list = new List();
    	List list2 = new();  //前面已知了类型,后面可省略
    	int[] arr = [1,2,3];      //C#12的集合表达式,方便的创建数组、集合
        List arr2 = [1,2,3];
    
        var n = 1;  //匿名类型,自动推断类型
        var list3 = new List;
    
        ref int n2 = ref n; //ref另一个变量的别名,n2、n实际指向同一个值,是等效的
    
        const int max =100; //常量
    
        var (name,age) = ("sam",18); //多个变量一起申明、赋值,这只是一种简化的语法糖
        (x, n) = (n, x);             //还可以用该语法交换变量值,非常优雅
    }
    

    3.1、const常量

    const 常量,顾名思义就是值永远不会改变的“变量”,可用于局部变量、字段。比如Math.PIInt.MaxValue,用于一些已知的、不会改变的值。

    • 申明常量的同时必须赋初始化值,不可修改,在编译时值会内联到代码中。
    • 常量只能用于C#内置的值类型、枚举,及字符串。
    • 常量值支持表达式,不过仅限于简单的运算,要能在编译时计算出确定的值。
    • 枚举其实也是常量。
    • 当用定义const 字段时,该常量字段就和静态字段一样,属于类本身,直接使用。
    const double r = 5.0;
    const double rs = 2 * Pi * r;
    

    📢 要注意常量(包括枚举)在编译时是把值内联到IL代码中的,因此如果跨程序集引用时,必须一起更新,否则就会出Bug。

    image.png

    3.2、ref 引用(别名/分身)

    ref 关键字的核心点就是引用另一个变量的地址,可看做是其别名(分身),指向同一地址。作用和指针操作比较相似,int* y = &x;,不过ref更安全、更方便。
    具体在使用上有以下一些场景:

    使用场景 说明
    引用传递参数 方法调用时传递引用参数,方法内可修改参数值 ,Foo(ref int number)
    ref return 返回一个ref变量,public ref int Foo(ref int n){return ref n;}
    ref 变量 引用另一个局部变量,ref int y = ref x
    ref 条件表达式 ref 用在三元表达式条件? ref (true):ref (fasle)中,返回引用
    ref struct struct完全分配在栈上、不能装箱,只能用于局部变量、参数,一些高性能的场景
    int x = 1;
    ref int y = ref x;  //x、y其实同一个变量
    Console.WriteLine($"{x},{y}"); //1,1
    x++;
    Console.WriteLine($"{x},{y}"); //2,2
    y++;
    Console.WriteLine($"{x},{y}"); //3,3
    
    //换个数组
    int[] arr = new int[] { 0, 1, 2};
    ref int a = ref arr[0];
    a=100;
    Console.WriteLine(arr); //100 1 2
    
    • ref readonly :所指向的变量不能修改值,但可以用ref重新分配一个reference 变量。
    • ref返回值:用于一个方法的返回值,返回一个变量的引用(别名)
    void Main()
    {
    	var arr = new int[] { 1, 2, 3 };
    	ref int f = ref GetFirst(arr);
    	f = 100;
    	Console.WriteLine(arr); //100 2 3
    }
    
    private ref int GetFirst(int[] arr)
    {
    	return  ref arr[0];
    }
    

    🔊 在某些场景使用ref可以避免值类型在传递时的拷贝操作,从而提高性能,不过不同场景不同,需要具体分析、经过性能测试再确定。


    04、常用(控制)语句

    语句 说明
    if 条件语句,if(true){ 执行 }
    if...else 条件语句,if(true){} else(){}
    if...else if...else 同上,中间可以接多个else if,不过这个时候一般建议重构下,比如用你switch模式匹配
    switch...case 根据条件处理多个分支:switch(条件){ case }。case命中后,注意break结束,否则会继续执行
    while(true){} 循环:条件为true就会循环执行
    dowhile(true) 循环:先执行后判断条件
    for循环 循环:for条件循环,支持多个语句逗号隔开。for(int i =0; i
    foreach in 循环元素:foreeach(int item in items),实现了IEnumerable,或有无参数 GetEnumerator()
    await foreach foreach的 异步版本
    List.ForEach() List自带的循环执行方法,list.ForEach(s=> s.Dump());
    break 跳出循环语句,for、foreach、while、switch、do。跳出最近的语句块,如果多层嵌套只会对最近的有效
    continue 继续下一次循环,只是后面的代码不执行了,应用条件同break
    return 结束方法/函数并返回结果(若有),注意是针对函数的。
    goto 跳转语句到指定标签,单独标签或者case值,一般不建议使用,goto可读性不太好
    throw 抛出异常,不再执行后面的代码
    try.catch.finally 异常处理,throw 抛出一个异常
    checkedunchecked 对整数运算语句进行溢出检查、不检查,如果检查溢出会抛出OverflowException
    fixed 申明指针固定一个可移动(回收)变量,防止被GC回收,在unsafe代码中运行
    stackalloc 在堆栈上分配内存,int* ptr = stackalloc int[10]
    lock 互斥锁 Monitor 的语法糖,保障同时只有一个线程访问共享资源 lock(obj){ }
    using 引用命名空间,释放IDisposable
    yield 用于迭代器中返回一个迭代值yield return value,或表示迭代结束yield break

    📢 switch 在C#8以上的更多特性,参考后文《C#的模式匹配

    4.1、try-catch异常处理

    一个标准的异常处理流程:

    • try:功能代码,需要捕获异常的地方。
    • catch:捕获异常,处理异常。支持多个catch语句,捕获不同的异常,多个catch按照顺序执行。catch后面可以用when表达式添加更多筛选条件。
    • finally:最后执行的代码,无论是否有异常发生都会执行,多用于最后的清理工作。
    • throw:可以抛出一个新的异常,也可以在catch直接throw;,保留原始堆栈信息。

    image

    	try
    	{
    		//功能代码
    		throw new ArgumentException("参数name为null");
    	}
    	//用when添加更详细的筛选条件
    	catch (ArgumentException e) when (e.InnerException ==null)
    	{
    		//处理异常,如记录日志
    	}
    	catch (Exception e)
    	{
    		//处理异常
    		throw; //直接throw,保留原始堆栈信息
    	}
    	finally
    	{
    		//最后执行的代码,无论是否有异常发生都会执行,多用于最后的清理工作
    	}
    

    📢异步(线程)中的异常一般不会抛出到调用线程(或主线程),只会在await,或获取Task.Result时才会被抛出来,更多可查看异步编程相关章节。

    4.2、using 的5种用法

    using 在C#中有很多中用途,常用来引用命名空间、简化释放资源。

    using 用途 说明
    using namespace 引用命名空间,比较常用,基本每个类都会使用。
    global using 项目全局引用,避免每个类都重复using 相同的命名空间。
    using 别名 using来创建命名空间或类型的别名,简化代码中的使用。
    using static 引入一个类型的静态成员、嵌套类型,代码中直接使用引入的静态成员。
    using 语句 using 语句可确保正确使用 IDisposable 实例,using(var r){},简化后无需括号

    📢 命名空间 namespace 用于组织代码(作用域)的主要方式,用关键字namespace来命名,可嵌套。C#10 中可以用文件范围命名空间,减少一层括号嵌套。

    • global using的最佳实现是一般创建一个公共的类文件“Usings.cs”,专门放置项目中全局的公共using
    • using来创建命名空间别名,使用时需要用到操作符::来访问下级。
    • using可创建任意类型的别名,包括数组、泛型、元祖、指针。
    global using System.Text; //全局引用命名空间
    
    using System.Text; //引用命名空间
    
    using json = System.Text.Json.JsonSerializer;  //类型别名
    using NumberList = double[];     //类型别名:数组
    using Point = (int X, int Y);    //类型别名:元祖ValueTuple
    using jsons = System.Text.Json;  //空间别名
    
    //namespace myspace; 效果同下,简化写法,可节省一对大括号
    namespace myspace
    {
    	public class Program
    	{
    		void Main()
    		{
    			json.Serialize(new Object());
    			jsons::JsonSerializer.Serialize(new Object()); //这用到操作符::
                NumberList arr = [1,2,3];
    		}
    	}
    }
    

    📢 从.Net6开始,C#项目会根据项目类型隐式包含一些using引用,比如SystemSystem.Text

    • using static,引入一个类型的静态成员、嵌套类型,代码中直接使用引入的静态方法。
    using static System.Math;
    
    void  Main()
    {
    	var a = Abs(-2 * PI ); //直接使用Math下的静态成员
    }
    

    🔸using 语句确保对象在using语句结束时被释放(调用Dispose)。也可以直接用using申明变量,不用大括号{},这是一种简化的写法,会在作用域(方法、语句块)结束时释放。

    using (StreamReader reader = File.OpenText("numbers.txt"))
    {
        Console.WriteLine("do read...");
    }
    // 简化写法,效果和上面一样,直接用using修饰 变量申明
    using StreamReader reader2 = File.OpenText("numbers.txt");
    
    //编译后的代码:
    StreamReader reader = File.OpenText ("numbers.txt");
    try
    {
        Console.WriteLine ("do read...");
    }
    finally
    {
        if (reader != null)
        {
            ((IDisposable)reader).Dispose ();
        }
    }
    

    📢 using语句是一种语法糖,会自动生成try...finally代码。


    参考资料


    ©️版权申明:版权所有@安木夕,本文内容仅供学习,欢迎指正、交流,转载请注明出处!原文编辑地址-语雀

  • 相关阅读:
    【JavaSE】一起学继承
    GO微服务实战第二十一节 如何实现接口限流和降级?
    css学习笔记
    Kotlin实现微信界面切换(Fragment练习)
    设计模式——结构型模式(静态代理、JDK动态代理)
    类的使用 重写父类方法 类中的修饰符 get 读取字段 set 对字段进行修改 抽象类(抽象 成员) 2.8类的初始化顺序
    CORS(跨域资源共享)
    包含Uniapp单聊思路,单群聊集群
    Dynamsoft Barcode Reader SDK JAVA.9.2.X
    视频监控汇聚平台LntonCVS视频集中存储平台解决负载均衡的方案
  • 原文地址:https://www.cnblogs.com/anding/p/18170347