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); //查看父类名称
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10

      - 变量

      - 变量的声明:var声明编译器会自动判断出来类型,一般都使用int,double等明确的标识数据类型
      - 变量的使用

      - 方法:处理数据的逻辑,又称“算法”

      - 方法的声明

      class Calculator
      {
       public int Add(int a, int b)  // 加入public,可以在类外面也能访问这个方法,默认是private,只能在类内访问
       {
            int result = a + b;
            return result;    
        }
      }
      1
      2
      3
      4
      5
      6
      7

      - 方法的调用

      - ```
      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型

变量、对象和内存

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

变量有七种:静态变量(属于类)、实例变量(成员变量、字段,默认是实例变量),数组元素,值参数,引用参数(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类型的实例在堆上

    1
    2
    3
    int x = 100;   // 存在栈内存
    object obj; // 存在栈内存
    obj = x; // 从x所在的栈内存复制值到堆内存,再存入堆内存的地址
  • 拆箱: 把堆上面object类型上实例的值按照要求拆成目标数据类型,然后存到栈上去

    1
    2
    // 上接拆箱
    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

  …

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

构造器

  • 构造器(constructor)是类的成员之一

  • 狭义的构造器指的是“实例构造器”(instance constructor)

    • 创建实例的时候就是调用了类的构造器,类的构造器会帮助初始化字段。(类似于python中的__init__),并允许多个构造器,默认会置0,整型就是0,字符串全是0的话就是null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 自定义构造器
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个签名,按上下键可以切换,一般情况根据传入的参数自动选择。
  
1
2
3
4
5
6
7
8
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操作符:在内存中创建一个类型的实例,并立刻调用这个实例的实例构造器,还可以调用这个实例的初始化器

    1
    Form myForm = new Form() { Text = "Hello"};  // 花括号中的就是初始化器 
    • 显示变量:明确说明是什么类型,比如Int
    • 隐式变量:用var定义,由赋值的时候编译器去判断是什么类型
    • 强类型语言,一旦确认了类型就不能再修改了
    • string类型是类,但是不用new操作符,这是个语法糖,其实底层用了
  • sizeof操作符:在内存中占用的字节 ,也可以获取自定义的类型大小,但是要在unsafe中执行

  • -> 操作符:指针访问操作符,C#中的指针要在unsafe中使用

  • 一元操作符:只有一个操作数

  • &x:取地址操作符,需要不安全的上下文,取数据在内存中的地址

    Student* pStu = &stu;

  • *x:引用操作符,需要不安全的上下文,拿到内存中地址所指的对象

    1
    (*pStu).Score = 100;   // 加括号是因为点操作符优先级高于引用操作符
  • + - 操作符:正号和负号,通过一个操作数来区分加减号

  • 前置++操作符:单独使用时和后置++相同,遇到赋值操作符时,会先算++,然后再赋值

  • (T)x:强制类型转换操作符

    • 隐式类型转换:由编译器区自动判断类型进行转换

      • 不丢失精度的转换:比如int型向long型转换,小范围向大范围数值转换
      • 子类向父类的转换:子类向父类传递时,只会看到父类中的方法
      • 装箱:值类型的实例从栈转移到堆里去
    • 显示类型转换

      • 有可能丢失精度(甚至发生错误)的转换,即cast(铸造,高精度向低精度转换):(T)x,T表示要转到的类型

      • 拆箱

      • 使用Conver:进行类型转换的工具类,一般情况下string不能向int类型转换,差别太大,就可以使用Conver

      • ToString方法与各数据类型的Parse/TryParse方法:Parse只能解析可以转换的数值,不符合格式的话直接报错,TryParse可以返回一个bool类型,告知是否可以转换

        1
        double x = double.Parse("12.0");
    • 自定义类型转换操作符

      1
      public static explicit operator Monkey(Stone stone)  // 定义了一个类型转换的方法,在Stone类中定义,explicit:显示类型转换
      1
      explicit
      1
      { Monkey m = new Monkey(); m.Age = stone.Age / 500; return m; }
  • << : 左移操作符,没有溢出的情况下,左移一位就是乘二,右移一位就是除二

  • is操作符:判断是否是一个类,继承下来的也算是同一个,类都是从object类型派生出来的, 但是派生出来的类和以前的不是同一个,子类属于父类,父类不属于子类

  • as操作符:如果是一个类,就把这个类的引用传递过去进行强制类型转换,如果不是,则传null

  • ?:可空类型修饰符,引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空,比如int x = null会报错,这时候可以使用可空操作符 int? x = null;

  • ??操作符

    1
    int y = x ?? 1;  // 如果x为null,则y赋值1,不为null,则是x本身
  • ?: 操作符

    1
    string str = x > 10 ? "Pass" : "Failed";

快捷键:

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

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

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


文章转载自:https://www.cnblogs.com/MJ-CAT/p/12019311.html