C#学习笔记(二)

表达式

编程语言的表达式:用于求值的表达实体,最小的算法元素

C#对表达式的定义: (一个或多个操作数)和(0个或多个操作符组成)

获取类型名称:xxx.GetType().FullName

语句

定义:最小的独立元素,表达式可以组成语句,语句是高级语言才有的,低级语言只有指令。

高级语言中的表达式对应低级语言中的指令,语句等价于一个或一组有明显逻辑关联的指令。

编程:使用语句编写程序

变量的作用域:

字段

什么是字段:

  • 一种表示与对象或类型(类或结构体)关联的变量
  • 字段是类型的成员(声明在方法里的是局部变量,不叫字段),旧称成员变量
  • 与对象关联的字段也称为“实例字段”
  • 与类型关联的字段称为静态字段,由static修饰

字段的声明:

  • 一定是名词

字段修饰符:

  • new
  • public:(可以修饰类也可以修饰成员)公共的,最高的访问级别,访问没有限制,命名大写字母开头
  • protected:(只修饰成员)受保护的, 只有在当前类内部及所有的子类内部可以访问。基类中访问无限制,子类中直接访问或通过子类实例访问。
  • private:(只修饰成员)私有的,最低的访问级别,被private访问修饰符修饰的成员只有在当前类的内部可以访问,其他地方一律不得访问,命名小写字母开头
  • static
  • readonly
    • 实例只读字段:只能在创建实例的时候可以初始化
    • 静态只读字段:静态构造器中初始化
  • volatile

this代表当前实例

字段的初始值:

  • 无显示初始化时,字段获得其类型的默认值,所以字段“永远都不会被初始化”
  • 静态字段初始化时期:类型被加载(load)时,静态构造器,加载这个类型的时候
    // 静态构造器
    Class Brush
    {
        public static readonly int a;
        static Brush()
        {
               Brush.a = 10;
        }
    }
  • 实例字段初始化时机:对象创建时

属性

什么是属性

  • 属性(property)是一种用于访问对象或类型特征的成员,为C#所特有
  • 属性是字段的自然扩展
    • 从命名上看,field更偏向于实例对象在内存中的布局, property更偏向于反应现实世界对象的特征 
      public int Age
      {
          get
          {
              return this.age;
          }
          set   // value是set中才有的关键字,代表用户传进来的值,可以在set中进行值检验
          {
              this.age = value;
          }
      }
      // 使用
      class.Age
    • 对外:暴露数据,数据是可以存储在字段里的,也可以是动态计算出来的
    • 对内:保护字段不被非法值污染
  • 属性由Get/Set方法对进化而来
  • 语法糖

属性的声明

  • 完整声明:propfull 加两下tab(也可以通过vs的refactor工具)
    class Student
    {
            private int myVar;   // 被属性包装的字段的名字
    
            public int MyProperty
            {
                get { return myVar; }
                set { myVar = value; }   // 可以在这里加代码以保护逻辑
            }
    }

     

  • 简略声明:通过简略声明声明出来的属性,功能上和一个公有的字段是完全相同的,好处在于声明起来比较简单,带有这种属性的类就是为了传递数据用的

    // prop 两下tab
    public int MyProperty {get; set;}

属性和字段的关系

  • 一般情况下,它们都用于表示实体(对象或类型)的状态
  • 属性大多情况下是字段的包装器(wrapper)
  • 建议:永远使用属性(而不是字段)来暴露数据,即字段永远是private或protected

索引器

索引器(indexer)是这样一种成员:它使对象能够用与数组相同的方式(即使用下标)进行索引,一般用在集合里面

索引器的声明

indexer 两下tab

常量

 const 修饰,隶属于类型而不是对象,没有实例常量,”实例常量“的角色由只读实例字段来担当。可以提高程序的可读性和执行效率。

参数

传值参数:声明时不带修饰符的形参是值形参

后续。。。。

委托

什么是委托

  • 委托(delegate):是函数指针的升级版,java中没有与委托相对应的功能实体
    • 实例:C/C++中的函数指针
      // 定义一个名为Calc函数指针,它指向了有两个参数,且返回整型的函数,并把它定义为一种数据类型
      typedef int(*Calc)(int a, int b); 
      // 使用, Add,Sub是已定义好的函数 
      Calc func1 = &Add; 
      Calc func2 = ⋐
      z = func1(1, 2);
  • 一切皆地址
    • 变量(数据)是以某个地址为起点的一段内存中所储存的值
    • 函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令
  • 直接调用与间接调用
    • 直接调用:通过函数名来调用函数,CPU通过函数名直接获得函数所在地址并开始执行,返回
    • 间接调用:通过函数指针来调用函数,CPU通过读取函数指针存储的值获得函数所在地址并开始执行,返回
  • 委托的简单使用
    • Action委托:用于没有返回值的方法
      // 声明
      Action action = new Action(目标方法名);
      // 使用1
      action.Invoke();
      // 使用2
      action();
    • Func委托: 用于有返回值的方法
      // 声明,可以没有参数
      func<参数1类型,参数2类型, 返回值类型> 委托名= new Func<参数1类型,参数2类型,返回值类型>(方法名);
      // 使用1
      委托名.Invoke(x, y);
      // 使用2
      委托名(x, y);

       

委托的声明(自定义委托)

  • 委托是一种类(class),类是数据类型,所以委托也是一种数据类型
  • 它的声明方式与一般类不同,主要是为了照顾可读性和C/C++传统
    // 自定义委托
    public delegate 返回值类型 委托类名(参数1类型 参数1名称, 参数2类型,参数2名称);
    // 自定义委托声明
    委托类名  委托实例名 = new 委托类名(目标方法名);
    // 自定义委托使用1
    委托实例名.Invoke(参数1,参数2);
    // 自定义委托使用2
    委托实例名();
  • 注意声明委托的位置
    • 应该声明在名称空间体内,与其他的类处在同一级别
  • 自定义的委托与所封装的方法必须”类型兼容“
    • 返回值的类型抑制
    • 参数列表在个数和数据类型上一致

委托的一般使用

  • 实例:把委托当做方法的参数传给另一个方法,形成一种动态调用方法的结构
    • 正确使用1:模板方法,”借用“指定的外部方法来产生结果:符合面向对象的开闭原则,即软件修改的时候,应该尽量用扩展进行变化,而不是通过修改已有的代码。
      • 使代码复用reuse,通过委托来调用相关的方法,如果扩展的话,只需要写在委托中就行了,不需要修改原来的类(python装饰器功能类似)
      • 把方法当做参数传递,达到动态调用的方式
    • 正确使用2:回调(callback)方法,调用指定的外部方法
    • 无论是模板方法还是回调方法,都是用委托类型的参数,封装一个外部方法,把这个方法传进方法的内部,再进行间接调用
  • 注意:难精通 + 易使用 + 功能强大,一旦被 滥用后果很严重
    • 缺点1:这是一种方法级别的紧耦合,现实工作中要慎用
    • 缺点2:可读性下降,debug难度增加
    • 缺点3:把委托回调,异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护
    • 缺点4:委托使用不当有可能造成内存泄漏和程序性能下降

委托的高级使用

  • 单播委托:一个委托封装一个方法的使用形式。
  • 多播(multicast)委托:一个委托内部封装了不止一个方法,也是同步调用。action1 += action2
  • 隐式异步调用
    • 同步与异步简介
      • 中英文有语言差异
      • 同步:A先做完B再接着A做
      • 异步:A和B同时做,相当于汉语中的同步进行
    • 同步调用和异步调用对比
      • 每一个运行的程序是一个进程(process)
      • 每个进程可以有一个或者多个线程
      • 同步调用是在同一线程内
      • 异步调用的底层机理是多线程
      • 串行 == 同步 == 单线程, 并行 == 异步 == 多线程
    • 隐式多线程 V.S. 显示多线程
      • 直接同步调用:使用方法名
      • 间接同步调用:使用单播/多播委托的Invoke方法
      • 隐式异步调用:使用委托的BegingInvoke,action1.BeginInvoke(Callback, object) BeginInvoke方法会自动的生成一条分支线程,在这个线程中去执行方法。callback参数是执行完后回调的方法,不需要可以为null,另一个参数一般为null
      • 显示异步调用:使用Thread或Task
  • 应当适时地使用接口(interface)取代一些对委托的使用
    • Java完全地使用接口取代了委托的功能

事件

初步了解事件

  • 定义:Event,能够发生的什么事情
  • 角色:使对象或类具备通知能力的成员
  • 使用:用于对象或类间的动作协调与信息传递(消息推送)
  • 原理:时间模型(event model)中的两个5
  • ”发生 >响应“中的5个部分:闹钟响了人起床,闹钟,响,人,起床,暗含了人订阅了闹钟响了的事件,共5个 
  • ”发生 >响应“中的5个动作:
    1. 我有一个事件(事件参数)
    2. 一个或一群人关心(订阅)这个事件(事件的订阅者)
    3. 我的事件发生了
    4. 关心这个事件的人会收到通知
    5. 收到通知的人会对事件进行影响(处理事件)
  • 提示
    • 事件多用于GUI界面开发
    • 各种编程语言对这个机制的实现方法不尽相同
    • java中没有事件这种成员。Java用接口来实现事件
    • MVC, MVP, MVVM等模式是事件模式的更高级,更有效的用法(事件模式牵扯到的元素太多,有5个)
    • 日常开发的时候,使用已有事件的机会比较多,自己声明的机会比较少

事件的应用

  • 事件模型的五个部分
    1. 事件的拥有者(event source , 对象)
    2. 事件成员(event, 成员, 事件本身,向订阅者发送通知 )
    3. 事件的响应者(event subscriber, 对象或类,拥有自己的事件处理器,订阅了事件的对象或类)
    4. 事件处理器(event handler,方法成员,回调方法),sender:事件的发送者
    5. 事件订阅——把事件处理器与事件关联在一起,本质上是一种以委托类型为基础的“约定”
  •  事件模型五个部分的不同组合
    1. 事件的响应者去向事件的拥有者订阅事件,两个类(MVC模式的雏形)
      using System;
      using System.Timers;
      
      namespace ConsoleApp2
      {
          class Program
          {
              static void Main(string[] args)
              {
                  Timer timer = new Timer(); // 事件的拥有者
                  timer.Interval = 1000;  // 1000ms执行一次
                  Boy boy = new Boy();    // 事件的响应者
                  // 事件: timer.Elapsed       
                  timer.Elapsed += boy.Action;  //事件 订阅(+=)  事件处理器 
                  timer.Start();
                  Console.ReadLine();
              }
          }
      
          class Boy
          {
              // 事件的处理器
              internal void Action(object sender, ElapsedEventArgs e)
              {
                  Console.WriteLine("Jump");
              }
          }
      }
      View Code
      using System;
      using System.Windows.Forms;
      
      namespace netframework
      {
          class Program
          {
              static void Main(string[] args)
              {
                  Form form = new Form();  //  事件拥有者
                  Controler controler = new Controler(form);  // 事件响应者
                  form.ShowDialog();
              }
          }
      
          class Controler
          {
              private Form form;
              public Controler(Form form)
              {
                  if (form != null)
                  {
                      this.form = form;
                      this.form.Click += this.FormClicked;  // 事件  订阅   事件处理器
                  }
              }
              // 事件处理器
              private void FormClicked(object sender, EventArgs e)
              {
                  this.form.Text = DateTime.Now.ToString();
              }
          }
      }
      View Code
    2. 事件的拥有者同时也是事件的响应者 ,拥有者和响应者属于同一个类
      using System;
      using System.Windows.Forms;
      
      namespace netframework
      {
          class Program
          {
              static void Main(string[] args)
              {
                  MyForm form = new MyForm();     // 事件拥有者   事件响应者
                  form.Click += form.FormClicked;//  事件 订阅 事件处理器
                  form.ShowDialog();
              }
      
          }
      
          class MyForm : Form
          {
              internal void FormClicked(object sender, EventArgs e)
              {
                  this.Text = DateTime.Now.ToString();
              }
          }
      }
      View Code

       

    3. 事件的拥有者是事件的响应者的一个字段成员,事件的响应者用自己的一个方法订阅了自己成员的某个事件,也是windows平台上默认的事件订阅处理结构,意义最大
      using System;
      using System.Windows.Forms;
      
      namespace netframework
      {
          class Program
          {
              static void Main(string[] args)
              {
                  MyForm form = new MyForm();    // 事件响应者
                  form.ShowDialog();
              }
      
          }
      
          class MyForm : Form
          {
              private Button button;      // 事件拥有者
      
              public MyForm()
              {
                  this.button = new Button();
                  this.Controls.Add(this.button);
                  this.button.Click += this.ButtonClicked; // 事件  订阅  事件处理器
              }
      
              // 事件处理器
              private void ButtonClicked(object sender, EventArgs e)
              {
                  this.Text = DateTime.Now.ToString();
              }
          }
      }
      View Code

 

 

  •  事件的本质是委托字段的一个包装器
    • 这个包装器对委托字段的访问起限制作用
    • 封装的一个重要功能就是隐藏
    • 事件对外界隐藏了委托实例的大部分功能,仅暴露添加/移除事件处理器的功能
    • 添加/移除事件处理器的时候可以直接使用方法名,这是委托实例所不具备的
  • 用于声明事件的委托实例的命名约定
    • 用于声明Foo事件的委托,一般命名为FooEventHandler(除非是一个非常通用的事件约束)
    • FooEventHandle委托的参数一般由两个(由WIn32 API演化而来,历史悠久)
      • 第一个是object类型,名字为sender,实际上就是时间的拥有者、事件的source
      • 第二个是EventArgs类的派生类,类名一般为FooEventArgs,参数名为e。也就是前面讲的事件参数。
    • 触发Foo事件的方法一般命名为OnFoo,即因何引发
      • 访问级别为protected,不能public,不然又成了可以"借刀杀人"
  • 事件的命名约定
    • 带有时态的动词或者动词短语
    • 事件拥有者"正在做"什么事情,用进行时; 事件拥有者做完了什么事情,用完成时
  • 注意
    • 时间处理器是方法成员
    • 挂接处理器(使用+=挂接)的时候,可以使用委托实例,也可以直接使用方法名,这是个语法糖-
    • 事件处理器对事件的订阅不是随意的,匹配与否由声明事件时所使用的委托类型来检测
    • 事件可以同步调用也可以异步调用

 事件的声明(自定义声明)

  •  声明
    • 完整声明
      // 委托声明
      public

       

    • 简略声明

 

快捷键:

完整声明属性:propfull 两下tab

简略声明属性:prop 两下tab

索引器声明:indexer 两下tab

visual studio中的图标: 小闪电--事件(一定条件下可以通知谁,通知别人),小方块---方法(能做什么,做事情), 小扳手--属性(处于什么状态,存储数据),类的最重要的三块。

posted @ 2020-02-03 15:56  麻酱猫  阅读(229)  评论(0编辑  收藏  举报