栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

c# 笔记

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

c# 笔记

记录c# 入门级笔记


文章目录
  • .Net 生态
  • 栈与堆
    • 函数指针
  • 运算符
    • 1. 位移 >> << 左位移 、右位移
    • 2. & 、| 、^ 运算符 基于二进制数 做运算
  • 属性
  • c# 索引器的理解
    • 索引器与属性的区别
  • 传参方式
    • 值类型方式
      • 值类型参数
      • 引用类型参数
    • 引用类型传参方式
      • 值类型参数 && 引用类型参数
    • 总结 :
  • 输出参数
  • 数组参数 params
  • 函数调用
    • 直接调用
    • 间接调用
  • 委托
  • 事件
    • 事件模型五个组成部分
    • 事件声明完整写法
    • 事件声明渐变写法
    • 事件和委托的关系及区别
  • 面向对象
    • 重写
  • 插件式设计编程
  • 枚举比特位用法


.Net 生态

.Net 发展历程
基础类库源代码

一些实用的资源

  • .NET指南– Microsoft针对新手的常规文档;
  • .NET Core指南–适用于那些正在考虑过渡到Core版本的用户;
  • .NET Framework –.NET的原始文档;
  • Xamarin –移动开发文档;
  • .NET Foundation– .NET的开源社区;
  • NuGet–可查看所有.NET的软件包;
  • .NET Core和Framework –提供下载链接。
  • .NET开发人员社区–开发人员社区和支持,其中包括:指向社交媒体、论坛、博客、实时研讨会、以及诸如.NET Foundation之类组织的链接。
栈与堆

简单来说,值类型和引用类型变量本身在栈中分配内存,引用类型的实例在堆中分配内存。

  1. 引用类型变量中存储的值是堆内存上 实例存储地址(内存编号);
  2. 堆上存储这实例的各个变量数值,并且与栈上的存储数值形式一样

调用方法的时候也会在内存中占有空间

  1. 方法参数都会存在栈或堆内存中,它的归属者就是 它的调用者。
  2. 方法内部的局部变量归属于当前方法。
  3. 方法 是以为某个地址为起点的一段内存中所存储的一组机器语言指令

备注 : cpu寄存器 : cpu中的一块小内存区域; 如果寄存器内存不足 则向内存栈中开辟空间 c# 方法的返回值都存在 寄存器中。

栈溢出和内存泄漏是 两回事

  1. 栈溢出是 栈内存不够用了
  2. 内存泄漏是 堆内存出现了 占用内存的漏洞,对象没有及时被释放的情况
函数指针

一切皆地址

不论函数与变量都是直接指向的内存地址,所以称为一切皆地址

运算符 1. 位移 >> << 左位移 、右位移

位移理解可能简单一些:其实就是数据转换成二进制的左右移动;右移左补0,左移右补0,后面多出来的部分去掉。
用乘除法去理解位移也可以:
左位移:相当于乘
左移1位相当于乘2,左移2位相当于乘4,左移3位相当于乘8,左移4位相当于乘16…类推
右位移:相当于除
右移1位相当于除2,右移2位相当于除4,右移3位相当于除8,右移4位相当于除16…类推

2. & 、| 、^ 运算符 基于二进制数 做运算 属性

属性是对字段的进化、也可以理解为封装。 目的是为了对字段起到保护、和扩展字段逻辑的措施。

c# 索引器的理解

通常情况下,属性只能访问单一的字段(一个属性对一个字段进行封装),如果想访问多个数据成员,就需要使用索引器。索引器是类的特殊成员,它可以根据索引在多个成员中进行选择,能够让对象以类似数组的方式来存取。索引器的数据类型必须是统一的。
备注 : 索引器与属性的概念很类似 get;set; 用法一致

索引器与属性的区别

索引器参数概览

  1. 使用索引器可以用类似于数组的方式为对象建立索引。
  2. get 取值函数返回值。 set 取值函数分配值。
  3. this 关键字用于定义索引器。
  4. value 关键字用于定义由 访问器分配的值。 (value参数类型与定义索引器的数据类型是一致的)
  5. 索引器不必根据整数值进行索引;由你决定如何定义特定的查找机制。
  6. 索引器可被重载。
  7. 索引器可以有多个形参,例如当访问二维数组时。
传参方式 值类型方式 值类型参数

每次传参方法内部都会创建一个参数的副本 也就是创建出来一个新的副本(占用新的内存空间)
备注: 由于值类型存储在栈内存上的,并且值类型变量所对应的value就是变量值。 所以会出现 方法内的值类型变量更新不影响外部值类型参数。

引用类型参数

每次传参方法内部也会创建一个参数的副本 也会跟值类型的方式一样。
但是 由于引用类型变量 是在栈上存储着一个堆内存上的地址, 所以会出现 两个引用类型变量虽然 是两个不同的变量但他们的值都指向的同一个堆内存地址。

引用类型传参方式 值类型参数 && 引用类型参数

他们传参的时候 内部不会再创建新的参数副本了, 会直接引用实参的内存地址进行传参。

总结 :

ref 关键字在本质在于传递的内存地址的不同。
值类型方式传递 是会创建新的内存地址
引用类型方式传递不会创建,而是直接引用实参的内存地址
虽然结果一样 咱底层的内存机理是不一样的。

输出参数

场景: 在方法返回值无法满足你的需求时, 输出参数也可以返回数据。
输出参数与ref类似 在输出参数时,不会在内存中创建副本且使用前不用为输出参数赋值。

数组参数 params

数组可以作为实参传递给方法形参。 由于数组是引用类型,因此方法可以更改元素的值。

函数调用

一切皆地址
1. 变量(数据) 是以某一地址为起点的一段内存中所存储的值
2. 函数(算法) 是以某一地址为起点的一段内存中所存储的一组机器语言指令

函数分为直接调用和间接调用

直接调用

通过函数名来调用函数, cpu通过函数名直接获取函数所在地址并开始执行

间接调用

通过函数指针来调用函数, cpu通过函数指针存储的值获取函数所在地址并开始执行

委托

委托是一种类 , 类是数据类型所以委托也是一种数据类型; 他的声明与类不同,语法参照 c/c++ 函数指针的语法
委托是 c/c++语言函数指针的升级版
委托数据类型

事件 事件模型五个组成部分

事件声明完整写法
    /// 
    /// 声明委托类型    点菜委托
    /// 
    /// 事件拥有者
    /// 委托消息参数
    public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
    
    public class Customer
    {
        /// 
        /// 这部分就是 事件所谓的  事件是基于委托的   (事件的完整声明)
        /// 
        private OrderEventHandler orderEventHandler;

        public event OrderEventHandler Order 
        {
            add {
                this.orderEventHandler += value;
            }
            remove
            {
                this.orderEventHandler -= value;
            }
        }
    }
事件声明渐变写法
        // 声明事件的简化方式 (容易理解成事件是字段的错觉)
        public event OrderEventHandler Order;
事件和委托的关系及区别

  1. 事件是基于委托的 但并不是一个特殊的委托, 基于委托做类型约束的;
  2. 当事件的响应者向事件的拥有者提供了事件处理器之后 能够将事件处理器能够存储起来 (此处只有委托可以实现, 因为委托可以引用方法)
  3. 因为委托是一个可以新增、移除、调用的方法成员 可操作性高; 而事件则是委托的封装后的成员类型(包装器),外部只允许 += 、 -= 操作符 事件拥有者内部可以直接使用到该事件的委托进行操作。 进一步提高了 委托的安全性。(由于c# 有个语法糖 让我们在内部可以用委托的方式来使用事件,其实是个语法糖,编译器会生成该委托)
// 事件原则上只可以使用 "+= , -=" 操作符的。 
// 事件拥有者内部可以通过事件使用其他操作符的原因是因为 
// 此处有个语法糖 编译器会把事件编译为 委托数据类型让你使用。 所以才会出现以下现象


public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);

public class Customer
{
        // 声明事件的简化方式 (容易理解成事件是字段的错觉)
        public event OrderEventHandler Simplify_Order;
        // 思考 点菜的动作执行
        public void Think()
        {
            if (this.Simplify_Order != null)
            {
                OrderEventArgs orderEventArgs = new OrderEventArgs();
                orderEventArgs.DishName = "宫保鸡丁";
                orderEventArgs.Size = "small";
                // 触发事件
                this.Simplify_Order.Invoke(this, orderEventArgs);
            }
        }
}

例如:window form 事件组成模式

事件拥有者属于事件响应者的一个成员字段。 并在事件响应者中为事件拥有者成员字段 订阅事件处理器。

自定义事件实例:

using System;
using System.Threading.Tasks;
using System.Threading;
namespace 自定义事件
{
    class Program
    {
        static void Main(string[] args)
        {
            // 普通事件组成模式
            Customer customer = new Customer();
            Watier watier = new Watier();
            customer.Order += new OrderEventHandler(watier.Action);
            // 事件触发 是事件拥有者内部的逻辑来触发
            customer.Action();
            customer.PayTheBill();
        }
    }

    // 示例场景 : 顾客点菜, 服务员下单的一个场景  事件触发逻辑 顾客点菜后服务员下单的事件


    /// 
    /// 声明委托类型    点菜委托
    /// 
    /// 事件拥有者
    /// 委托消息参数
    public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
    /// 
    /// 事件消息参数  主要用于传递所需的事件参数
    /// 
    public class OrderEventArgs : EventArgs
    {
        /// 
        /// 菜名
        /// 
        public string DishName { get; set; }
        /// 
        /// 菜量
        /// 
        public string Size { get; set; }

    }
    /// 
    /// 顾客   动作: 账单支付、 点菜的事件
    /// 
    public class Customer
    {
        /// 
        /// 这部分就是 事件所谓的  事件是基于委托的   (事件的完整声明)
        /// 
        private OrderEventHandler orderEventHandler;

        public event OrderEventHandler Order 
        {
            add {
                this.orderEventHandler += value;
            }
            remove
            {
                this.orderEventHandler -= value;
            }
        }
        // 声明事件的简化方式 (容易理解成事件是字段的错觉)
        public event OrderEventHandler Simplify_Order;
        /// 
        /// 账单价格
        /// 
        public double Bill { get; set; }

        public void PayTheBill()
        {
            // 我会支付这盘菜
            Console.WriteLine("I whill pay the dish ${0}", this.Bill);
        }

        public void Walkln()
        {
            // 走进餐厅
            Console.WriteLine("Walk into the restaurant");
        }
        public void Sitdown()
        {
            Console.WriteLine("坐下");
        }
        // 思考 点菜的动作执行
        public void Think()
        {
            for (int i = 0; i < 5; i++)
            {
                //想想
                Console.WriteLine("Let me think.......");
                Thread.Sleep(1000);
            }
            // 判断是否 有服务生订阅了事件
            if (this.orderEventHandler !=null)
            {
                OrderEventArgs orderEventArgs = new OrderEventArgs();
                orderEventArgs.DishName = "鱼香肉丝";
                orderEventArgs.Size = "small";
                this.orderEventHandler.Invoke(this, orderEventArgs);
            }
            if (this.Simplify_Order != null)
            {
                OrderEventArgs orderEventArgs = new OrderEventArgs();
                orderEventArgs.DishName = "宫保鸡丁";
                orderEventArgs.Size = "small";
                this.Simplify_Order.Invoke(this, orderEventArgs);
            }
        }

        public void Action()
        {
            this.Walkln();
            this.Sitdown();
            this.Think();
        }
    }
    /// 
    /// 服务生
    /// 
    public class Watier
    {
        /// 
        /// 事件处理函数
        /// 
        /// 
        /// 
        public void Action(Customer customer, OrderEventArgs e)
        {
            Console.WriteLine("I will serve you the dish -{0}", e.DishName);
            double price = 10;
            switch (e.Size)
            {
                case "small":
                    price = price * 0.5;
                    break;
                case "large":
                    price = price * 1.5;
                    break;
                default:
                    break;
            }
            //总账单
            customer.Bill += price;
        }
    }



}

类三大核心成员

  1. 属性: 存储数据
  2. 方法: 做事情
  3. 事件: 通知其他对象
面向对象

重写

overrid 和 hide 方法的区别
overrid 会在出现is a 概念的情况下一直沿着继承链从高到低(从base开始) 找到最新的子类的方法,当中如果出现子类有hide影响则中断从继承链的查找了,就断在Car类这个Run方法了,最后会执行 Car的Run函数


  1. 编程中 把确定的不易改变的成员封装起来, 把易改变的成员抽象起来为抽象成员
    如果不是需要修改BUG或添加新功能就不应该随意更改代码
    抽象类就符合 开放/关闭原则

接口则是面向对象中最抽象化的实现了, 好处: 极大的抽象化、 加大了代码的 “高内聚低耦合”

耦合关系图

依赖倒置的理解:
假设场景: 司机开车。
一般依赖关系: 司机开车需要依赖car车 卡车开车需要依赖trucker卡车 这是属于一般的逻辑。
使用接口后的依赖关系: 司机抽象依赖于IVehicle交通工具的接口, 而car汽车、trucker卡车依赖于IVehicle交通工具并实现了交通工具的抽象。 形成了我们一般逻辑上的反转。 让司机依赖于抽象、不同车型依赖于交通工具的抽象。
两者的区别: 第一种 具体实现依赖于具体实现 = 汽车司机依赖于汽车、 卡车司机依赖于卡车; 而第二种则是抽象依赖于抽象 司机抽象起来、交通工具抽象起来, 司机抽象依赖于交通工具的抽象最终实现 最大程度上的低耦合关系 也符合面向对象 设计五大原则

接口显式实现
只有用接口类型变量来引用实例时才可调用此接口函数。其他类型无法调用

插件式设计编程
  1. 主程序如果希望自身的功能可以被扩展,其需要:
    1.1 提供一组服务 (Service Interface)。其提供(约束)了插件对主体能力可控制的边界。服务定义的越精细,插件控制的粒度越小,能力越大,但耦合度和复杂度也越高。
    1.2 定义一种扩展契约 (Plug-In Interface),其描述了插件应该如何设计以便于主程序发现。并通过插件管理模块 (Plug-In Manager) 来发现、维护插件。
  2. 插件通过实现主程序规定的扩展契约(通常是一个接口),标明自己的身份,并接收来自主程序的事件响应。通过调动主程序提供的服务,实现和主程序的交互。这一过程,通常都是被主程序以 SDK (Software Development Kit) 的形式封装。基本上我们日常所有能接触到的应用都或多或少的提供了插件机制,比如操作系统。其上面运行的应用程序即可以理解为它的插件实现。

反射实现松耦合的最大化则是 插件式编程

插件式编程的好处: 是主体程序为中心, 把插件用于主题程序结合使用。 做到 以不变应万变的情况。

实现架构图

插件机制原理

常用插件式架构模式

枚举比特位用法
    enum Level
    {
        Employe,
        Manger,
        Boos,
        BigBoss
    }
    // 枚举赋多个值
    Level res = Level.BigBoss | Level.BigBoss | Level.Manger;
    // 判断是否有这个等级
    if ((res & Level.Boos)== Level.BigBoss)
    {

    }
    

此处用了 | & 两个二进制操作符

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/985302.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号