zoukankan      html  css  js  c++  java
  • C#学习笔记(一)

    C#完全面向对象,一个项目也是一个类

    名称空间(namespace):类似python中的包,但是也可以不使用using,直接使用名称空间

    using:引入名称空间,类似python中的import,方便管理类,也防止类的名称相同引起的冲突

    ctrl+. 查看使用名称空间

    dll:类库

    dll引用:黑盒引用,看不到源码

    项目引用:白盒引用

    nuget : 引用网上的类库

    一个project可以被多个solution所引用

    添加project:在vs中右击solution,Add,Existing Project(现有项)

    类(或对象)之间的耦合关系。追求高内聚,低耦合

    UML(通用建模语言) 类图:展现类和类之间的关系

     Program类依赖于Console类。

    从现实世界抽象到类的过程叫建模

    编程唯物主义

    可以定义类变量,之后再实例化,比如 类  变量名;

    com组件是非托管对象,可以不需要.net框架而直接运行,.net框架组件是托管对象,必须有.net框架的支撑才能运行

    暂时理解:C#中的引用是浅拷贝,传递的是引用,修改引用后的值会影响原来的值。

    .NET Framework和.NET Core

    类的三大成员:属性,方法,事件。

    事件(event):类或对象通知其他类或对象的机制,为C#所特有,善用事件机制非常重要

    在visual studio中,点击一下某个类,按F1就能进入这个类的定义

    特殊类或对象在成员方面侧重点:

    • 模型类或对象重在属性,如Entity Framework
    • 工具类或对象重在方法,如Math,Console
    • 通知类或对象重在事件,如各种Timer
    • 静态(Static)成员在语义上表示它是“类的成员”
    • 实例(非静态)成员在语义上表示它是“对象的成员”
    • 绑定(Binging)指的是编译器如何把一个成员与类或对象关联起来(晚绑定:动态语言,早绑定)
      •   . 操作符——成员访问操作符

    构成C#语言的基本元素

    精通:了解常用的元素的运行机制

    • 关键字(Keyword):学习的时候按照逻辑分组来学
    • 操作符(Operator)
    • 标识符(Identifier)
      • 什么是合法的标识符:下划线或字符开头,非要用关键字可以在前面加上@
      • 大小写规范
      • 命名规范:变量名用小驼峰法,类、方法名、命名空间用大驼峰法,方法名应该是一个动词或者动词短语
    • 标点符号
    • 文本(字面值)
      • 整数:长整型后面加个L(64位 long,短整型32位 int)
      • 实数(小数):float单精度(32位后缀加F),double双精度(64位,后缀加D)
      • 字符: char,用单引号
      • 字符串:string,用双引号
      • 布尔:true,false
      • 空(null)
    • 注释://,  /*  */

    上面的五种叫标记(Token):对于编译器有意义的字符

    • 类型(Type):也叫数据类型
      • Type myType = type(Forms);
        PropertyInfo[] plnfos = myType.GetProperties();  //查看所有的属性
        MethodInfo[] mlnfos = myType.GetMethods();  //查看所有的方法
        Console.WriteLine(myType.BaseType.FullName);  //查看父类名称
    • 变量
      • 变量的声明:var声明编译器会自动判断出来类型,一般都使用int,double等明确的标识数据类型
      • 变量的使用
    • 方法:处理数据的逻辑,又称“算法”
      • 方法的声明 
        • class Calculator
          {
               public int Add(int a, int b)  // 加入public,可以在类外面也能访问这个方法,默认是private,只能在类内访问
               {
                    int result = a + b;
                    return result;    
                }
          }
      • 方法的调用
        • Calculator c = new Calculator();
          int x = c.Add(2, 3);
          Console.WriteLine(x);
    • 程序 = 数据 + 算法

    强类型语言与弱类型语言的:数据受到数据类型的约束,就是强类型语言(java,C#),弱类型:js

    • C#为了学习弱类型语言,引入了dynamic关键字,可以不通过强制类型转换就可以进行数据赋值

    内存分配

    • Stack(栈)简介:给函数调用使用的,比较小
    • Stack overflow:栈溢出,死循环会造成栈溢出
    • Heap(堆)简介:存储对象,比较大
    • 使用Performance Monitor查看进程的堆内存使用量,WIN+R运行perfmon, 选择Process(进程),Private Bytes(不与其他处理共享的,已分配的当前字节数)
    • 关于内存泄漏:指堆分配内存后没有回收
    • 内存是以字节为单位,每个字节有自己的地址号

     C#的类型系统

    五大数据类型

    • 类(Classes): 如object, string, Windows,Form,Console
    • 结构体(Structures): 如bool, byte. Int32, Int64,Single
    • 枚举(Enumerations):enum定义的类型,只能从固定的几种定义中选取一种
    • 接口(Interfaces)
    • 委托(Delegates)

     C#类型的派生谱系

     列表定义:List<int>  :定义一个列表,里面存int型

     变量、对象和内存

    变量表示了存储位置,变量名对应着变量的值在内存中的存储位置

     变量有七种:静态变量(属于类)、实例变量(成员变量、字段,默认是实例变量),数组元素,值参数,引用参数(ref定义),输出形参(out定义),局部变量

     值类型的变量

    • byte/sbyte/short/ushort:
      • byte一个字节(0到255)
      • sbyte带符号的一个字节(-128到+127,计算机中负数是以补码存储的,反码:正数反码是本身,负数反码是除符号位的其他位0变1,1变0,补码:正数是本身,负数的补码是:符号位为1,其余各位求反,末位加1)
      • ushort:无符号短整型(0~65536),两个字节,高八位(左八位)存在高地址(大一点的地址)
      • short:有符号短整型(-32768~32767)
    • 值类型没有实例, 所谓的“实例”与变量合二为一。即int x; 而不是Int x =new Int();

     引用类型的变量与实例

     引用类型定义的时候给引用类型分四个字节(栈内存,用于存堆内存的首地址,刚开始全部置0),实例化的时候会根据类中的值类型再次分配相应的大小(分配到堆内存),然后将栈内存中的的四个字节改成堆内存的首地址,传递实例也是传递的指针。

    局部变量:分配在栈内存

    默认值:引用类型中的成员变量在堆内存中分配好之后就会被置0,本地变量不允许未赋值就打印(C#不允许)。

    常量:const修饰

    装箱与拆箱(Boxing & Unboxing):

    • 装箱:当引用类型发现引用的值不在堆内存中,而是一个栈内存中的值的时候,会把栈内存中的值先copy到堆内存中,然后在写入这个堆内存中的内存地址-----把栈上的值类型的值封装成object类型的实例在堆上
      int x = 100;   // 存在栈内存
      object obj;    // 存在栈内存
      obj = x;        // 从x所在的栈内存复制值到堆内存,再存入堆内存的地址
    • 拆箱: 把堆上面object类型上实例的值按照要求拆成目标数据类型,然后存到栈上去
      // 上接拆箱
      int y = (int)obj;

      现在用的少了,有性能损失。

     对象内存分配遵循以下两条原则: 

    • 引用类型总是分配到堆内存。 
    • 值类型和指针的分配与它们声明位置有关系

     方法的定义、调用和调试

    方法的由来

    •  方法(method)的前身是C/C++语言的函数(function)
    • 永远都是类(或结构体)的成员,纯面向对象语言,不能有方法独立于类之外
      • C#语言中函数不可能独立于类之外
      • 只有作为类(结构体)的成员时才被称为方法
      • C++中可以,称为全局函数
    •  是类的最基本成员之一(另一个是字段--变量)
    •  为什么需要方法和函数
    1. 隐藏复杂的逻辑
    2. 把大算法分解为小算法
    3. 复用(reuse)

     方法的声明和调用

     method-declaration(声明)

      method-header method-body

    method-header(只列出了常用的)

      method-modifiersopt(opt表示非必须)  return-type member-name(动词或动词短语) type-parameter-listopt(formal-parameter-listopt即形参)

    method-modifiers:

      method-modifier

      method-modifiers method-modifier

    method-modifier:

      new

      public

      protected

      static

      ...

    public static double xxx() // 公共方法,可在外部调用,静态方法,由类使用
    {
          ;      
    }

    构造器

    • 构造器(constructor)是类的成员之一
    • 狭义的构造器指的是“实例构造器”(instance constructor)
      • 创建实例的时候就是调用了类的构造器,类的构造器会帮助初始化字段。(类似于python中的__init__),并允许多个构造器,默认会置0,整型就是0,字符串全是0的话就是null
      •  // 自定义构造器
            class Student
            {
                public Student(int initId, string initName)   // 构造器
                {
                    this.ID = initId;
                    this.Name = initName;
                }
                public Student()   // 另一个构造器,会根据传入的参数进行自动选择
                {
                    this.ID = 1;
                    this.Name = "No name";  //string,引用类型,存引用,去堆中创建
                }
        
                public int ID;
                public string Name;
            }
    • 构造器的内存原理
      • 如果是带参数的构造器,会先在栈内存中分配四个字节,然后根据参数类型个数分配相应的堆内存,如果参数中有string,则会再开辟一块内存存值,之前分配的存地址
    •  方法的重载(Overload)
      • 调用重载方法
      • 声明带有重载的方法
        • 方法签名(method signature)由方法的名称、类型形参的个数和他的每一个形参(按从左往右的顺序)的类型和种类(值、引用ref或输出out)组成。方法签名不包含返回类型。方法的名字可以一样,签名不能一样。比如Console.WriteLine()有19个签名,按上下键可以切换,一般情况根据传入的参数自动选择。
          public int Add(int a, int b)
          {
                return a + b;
          }
          public double Add(double a, double b)
          {
                 return a + b;
          }
        • 重载决策(到底调用哪一个重载):用于给定了参数列表和一组后选函数成员的情况下,选择一个最佳函数成员来实施调用。
    •  如何对方法进程debug
      • 设置断点(breakpoint)
      • 观察方法调用时的call stack:查看调用情况
      • Step-in(跟踪到被调函数里边去,也叫step-into,F11),Step-over(不进入子函数,F10), Step-out(退出当前被调函数)
      • 观察局部变量的值与变化
    •  方法的调用与栈
      • 方法调用时栈内存的分配(主调用与被调用)
        • 对stack frame(一个方法在被调用的时候在内存中的布局)的分析:谁调用,谁负责压栈(不同的语言情况不同)
        • 未细看

     操作符

     优先级从上往下一次降低

      •  操作符不能脱离与它相关联的数据类型,整数的除号和浮点数的符号就不同
      • 操作符的本质是函数(即算法)的“简记法”,比如想使用加号,只需要把方法名改成关键字operator +
      • 带有赋值功能的运算符的顺序是由右向左,比如x += y += z,先算y+=z, 再算x +=
    •  点操作符:成员访问操作符,访问外层名称空间中的子名称空间,访问名称空间中的类型,访问类型的静态成员,访问对象的成员
    • [ ]操作符:元素访问操作符,访问集合(数组和字典)中的元素,数组:int[ ] myIntArrat = new int[ ] {1,2,3};  myIntArray[0];   字典:Dictionary<string, Student> stuDic = new Dictionary<string, Student>();
    • 后置++操作符:遇到赋值操作符时, y = x++  先把x的值传递给y,然后再计算x++
    • typeof操作符:查看一个类型的内部结构(Metadata,元数据),Type t = typeof(int); 可以查看t.Namespace,t.GetMethods[]
    •  default操作符:帮助获取一个类型的默认值,int x = default(int), 结构体类型默认是0,引用类型的默认值是null,枚举类型(enum定义)中返回的是枚举类型中为0的,如果枚举类型没赋值,那就是第一个值,如果没有为0的,那就直接返回0,而非枚举类型中的值。内存块中其实都是0,只是根据不同的类型返回不同的值
    •  new操作符:在内存中创建一个类型的实例,并立刻调用这个实例的实例构造器,还可以调用这个实例的初始化器
      Form myForm = new Form() { Text = "Hello"};  // 花括号中的就是初始化器 
      • 显示变量:明确说明是什么类型,比如Int
      • 隐式变量:用var定义,由赋值的时候编译器去判断是什么类型
      • 强类型语言,一旦确认了类型就不能再修改了
      • string类型是类,但是不用new操作符,这是个语法糖,其实底层用了 
    •  sizeof操作符:在内存中占用的字节 ,也可以获取自定义的类型大小,但是要在unsafe中执行
    • -> 操作符:指针访问操作符,C#中的指针要在unsafe中使用
    •  一元操作符:只有一个操作数
    • &x:取地址操作符,需要不安全的上下文,取数据在内存中的地址
      Student* pStu = &stu;
    • *x:引用操作符,需要不安全的上下文,拿到内存中地址所指的对象
      (*pStu).Score = 100;   // 加括号是因为点操作符优先级高于引用操作符
    •  + - 操作符:正号和负号,通过一个操作数来区分加减号
    • 前置++操作符:单独使用时和后置++相同,遇到赋值操作符时,会先算++,然后再赋值
    • (T)x:强制类型转换操作符
      • 隐式类型转换:由编译器区自动判断类型进行转换
        • 不丢失精度的转换:比如int型向long型转换,小范围向大范围数值转换
        • 子类向父类的转换:子类向父类传递时,只会看到父类中的方法
        • 装箱:值类型的实例从栈转移到堆里去
      • 显示类型转换
        • 有可能丢失精度(甚至发生错误)的转换,即cast(铸造,高精度向低精度转换):(T)x,T表示要转到的类型
        • 拆箱
        • 使用Conver:进行类型转换的工具类,一般情况下string不能向int类型转换,差别太大,就可以使用Conver
        • ToString方法与各数据类型的Parse/TryParse方法:Parse只能解析可以转换的数值,不符合格式的话直接报错,TryParse可以返回一个bool类型,告知是否可以转换
          double x = double.Parse("12.0");
      •  自定义类型转换操作符 
        public static explicit operator Monkey(Stone stone)  // 定义了一个类型转换的方法,在Stone类中定义,explicit:显示类型转换
        explicit
        { Monkey m = new Monkey(); m.Age = stone.Age / 500; return m; }
    • << : 左移操作符,没有溢出的情况下,左移一位就是乘二,右移一位就是除二
    • is操作符:判断是否是一个类,继承下来的也算是同一个,类都是从object类型派生出来的, 但是派生出来的类和以前的不是同一个,子类属于父类,父类不属于子类
    • as操作符:如果是一个类,就把这个类的引用传递过去进行强制类型转换,如果不是,则传null 
    • ?:可空类型修饰符,引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空,比如int x = null会报错,这时候可以使用可空操作符 int? x = null; 
    • ??操作符
      int y = x ?? 1;  // 如果x为null,则y赋值1,不为null,则是x本身
    • ?: 操作符
      string str = x > 10 ? "Pass" : "Failed";

      

    快捷键:

    for 然后连敲两下tab,会出来for循环的结构

    ctor,然后一下tab,自动构建一个构造器框架

    使用unsafe:菜单栏PROJECT--->OperatorsExample Properties ---> Build --->  勾选Allow unsafe code 

    看一门语言的文档

    1. 找到reference(参考),里面有keyword

  • 相关阅读:
    Python range() 函数用法
    substr
    disable enable 所有其他表关联的外键
    sql with multiply where
    小米开源文件管理器MiCodeFileExplorer-源码研究(7)-Favorite收藏管理和SQLite数据库CRUD
    Centos安装FastDFS+Nginx(一天时间搞定)
    Centos安装FastDFS+Nginx(一天时间搞定)
    小米开源文件管理器MiCodeFileExplorer-源码研究(6)-媒体文件MediaFile和文件类型MimeUtils
    小米开源文件管理器MiCodeFileExplorer-源码研究(6)-媒体文件MediaFile和文件类型MimeUtils
    小米开源文件管理器MiCodeFileExplorer-源码研究(5)-AsyncTask异步任务
  • 原文地址:https://www.cnblogs.com/MJ-CAT/p/12019311.html
Copyright © 2011-2022 走看看