笔记 CSharp

简介

C# 是由微软公司发布的,由 C 和 C++ 衍生出来的面向对象的编程语言,运行于 .NET Framework 和 .NET Core 之上的高级程序设计语言,可跨平台

开源情况:完全开源

交互:C/S, B/S

适用于:桌面应用程序、Internet应用程序、手机开发、游戏

学习原则:学以致用

面向对象编程

功能技术

编码样式

简单操作

  • 折叠冗余代码:大括号折叠会引起作用域的变化, #region 和#endregion 则不会

  • 输入输出:Console.WriteLine() / ReadLine() 支持bool char int string 等多种数据

  • Ctrl+k+c注释;Ctrl+k+u取消注释

  • 类型转换:如果兼容,可以使用自动类型转换或者强制类型转换;否则可以 Convert.ToInt32(要转换的变量)

  • 命名空间引用方式:using 命名空间(类似于C++头文件)

  • 数据范围:

    byte(0-255),short(-32768-32767),int(-2*10^9-2*10^9【-2147483648-2147483647】),long(-9*10^18-9*10^18);
    float(-3.4*10^38-3.4*10^38),double双精度类型(-5*10^~+-1.7*10^308);
    decimal(-+7.9*10^28);

  • 1

创建新项目

  • 选择 VS 为编译器;

  • Windows forms/ WPF Application 都带界面,WPF 更新一些,先练习 Windows forms

  • 添加类后,添加命名空间并让类继承Form就能得到空窗口,可在C#窗口编辑器打开和编辑

    using System.Windows.Forms;
    namespace a
    {
         public partial class TimerWindow : Form
         {
             public TimerWindow(){}
         }
    }

  • 1

显示命令行

变量与常量

类型系统

C# 作为一种强类型语言,每个变量常量表达式都有一个类型, .NET 类库定义了内置数值类型和表示各种构造的复杂类型。 其中包括文件系统、网络连接、对象的集合和数组以及日期

分类1:可分为值类型和引用类型,其中structenum 为值类型,所有类型都以 Object 为基类

  • 值类型变量直接包含其值, 结构的内存在声明变量的任何上下文中进行内联分配, 对于值类型变量,没有单独的堆分配或垃圾回收开销

  • 值类型已密封。 不能从任何值类型派生类型

  • 但是,一个结构可以实现一个或多个接口。 可将结构类型强制转换为其实现的任何接口类型, 这将导致“装箱”操作,以将结构包装在托管堆上的引用类型对象内。 当你将值类型传递给使用 System.Object 或任何接口类型作为输入参数的方法时,就会发生装箱操作

  • 结构通常用作一小组相关变量的容器

  • 枚举定义的是一组已命名的整型常量

  • 引用类型创建对象时,会在托管堆上分配内存,变量只保留对对象位置的引用

  • 在分配内存和回收内存时都会产生开销,CLR 的自动内存用于执行回收

  • 在声明引用类型变量时,它将包含值 null,直到将其分配给该类型的实例,或者使用 new 运算符(interface除外)创建一个

  • 所有数组都是引用类型

  • 1

分类2:可分为内置类型和自定义类型,可以使用 structclassinterfaceenumrecord 构造来创建自己的自定义类型

  • 类Classes:如Windows, Form, String

    var myType = typeof(Form); // var 自动推导类型
    Console.WriteLine(myType.FullName); // System.Windows.Forms.Form
    Console.WriteLine(myType.IsClass); // True

  • 结构体Structures:如Int32, Double

    // 用F12直接跳转到Int的定义,发现Int是结构体类型,在定义中还可以直接看到 MaxValue、MinValue
        public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<int>, IEquatable<int>
        {
            internal int m_value;
    ​
            [__DynamicallyInvokable]
            public const int MaxValue = 2147483647;
    ​
            [__DynamicallyInvokable]
            public const int MinValue = -2147483648;
            ……
        }

  • 枚举Enumerations

    Form1 f1 = new Form1();
    f1.WindowState = FormWindowState.Normal;
    ​
    public enum FormWindowState
    {
        Normal,
        Minimized,
        Maximized
    }

  • 接口Interfaces

    • 接口的作用类似一个管道,你可以通过这个接口走过去访问方法

    • 接口的重点在接通,通过接口通向这个方法,委托的重点在于指,我指向这个方法

    MyClass myClass = new MyClass();
    ​
    // Declare and assign using an existing value.
    IMyInterface myInterface = myClass;
    // Or create and assign a value in a single statement.
    IMyInterface myInterface2 = new MyClass();

  • 委托Delegates

    • 委托和类、接口类似的,是一种特殊的类,所以和类一样,也是一种数据结构,也可以声明变量和创建实例,和普通类不同的是委托是“方法模板”,类是“对象模板”

    • 功能:委托是函数指针的“升级版”,C#通过委托这种数据类型保留了C++中与函数指针相对应的功能

    // 

  • 文本值:可以通过在数字末尾追加一个字母来指定数字文本应采用的类型,如1.2f

  • 泛型:可使用一个或多个类型参数声明的类型,用作实际类型(具体类型)的占位符,客户端代码在创建类型实例时提供具体类型

    // 如List<T>
    List<string> stringList = new List<string>();
    stringList.Add("String example");
    // compile time error adding a type other than a string:
    stringList.Add(4);

  • 隐式类型var 匿名类型 可以为NULL的值类型int?(在类型后加问号,即为可以包含null值的类型)

  • 编译时和运行时类型:编译时类型是源代码中变量的声明或推断类型。 运行时类型是该变量所引用的实例的类型

  • 1

变量、对象、内存

  • 变量表示了存储位置,并且每个变量都有一个类型,以决定什么样的值能够存入变量,计算机系统通过变量的类型来分配给它对应大小的内存空间

  • 字段是属性的雏形,可以赋任意值

    var p = new Person();
    p.Age = 10;

  • 引用类型占 4 个字节即 32 位,变量默认值为 0,全部 Bit 置 0 时意为这个变量没有引用任何实例

  • 1

字段、属性、索引、常量

字段

  • 概念:与对象或类型关联的变量,旧称“成员变量

  • 分类:与对象关联称“实例字段”,与类型关联称“static静态字段”

  • 只读字段:readonly int a; 只能在构造函数中分配值,一旦初始化后就不希望再改变的值

  • readonly 与 const:常量只能是简单类型,编译器通过const修饰符,用其值取代了使用它的变量,编译器知道常量的值;当希望成为常量的值的类型不能被常量声明接受时使用只读字段,只读字段可以是实例成员,在运行期间通过构造函数指定

  • 注意:使用只读字段作为类成员时,需要把 static 修饰符分配给该字段

  • 1

属性

  • 概念:一种语法糖,由Get、Set进化而来,用来访问对象或类的特征的成员

  • 属性与字段:属性是字段的自然扩展,两者都是具有关联类型的命名成员,不同的是属性不表示存储位置,只有访问器,访问器指定在它们的值被读取或写入时需执行的语句

  • 只读属性:只有get

  • 建议:永远使用属性(而不是字段)来暴露数据,即字段永远是 private 或 protected 的

    // 1.使用 Get/Set 来保护字段合法性
    private int num;
    public void SetNum(int value)
    {
        if(value > 0 && value < 100)
        {
            num = value;
        }
        else
        {
            throw new Exception("num value has error.");
        }
    }
    public int GetNum()
    {
        return num;
    }
    // a.SetNum(20);
    
    // 2.使用属性
    private int num;
    public int Num
    {
        get
        {
            return num;
        }
        set
        {
            if(value > 0 && value < 100)
        	{
           		num = value;
        	}
        	else
        	{
            	throw new Exception("num value has error.");
        	}
        }
    }
    
    var a = new A
    {
        Num = 1;
    };
    // a.Num = 20;

  • 动态计算值的属性:

    public bool CanWork
    {
        get
        {
            return num > 16;
        }
    }

  • 1

索引器

  • 作用:使对象能够用与数组相同的方式(即使用下标)进行索引

  • 语法:

    var p = new Person();
    p["age"] = 20;

  • 注意:没有静态索引器

  • 1

迭代器

  • 1

常量

  • 编译器通过const修饰符,用其值取代了使用它的变量,编译器知道常量的值

  • 成员常量与局部常量:

  • 语法:const int a;

  • 注意:常量只能是简单类型,不能是类/自定义结构体类型

  • 1

众多只读情况

  • 隶属于类型,为了提高程序可读性和执行效率 —— 常量

  • 只能在构造函数中初始化,为了防止对象的值被改变 —— 只读字段

  • 分为静态和非静态,向外暴露不允许修改的数据 —— 只读属性,功能与常量有一些重叠

  • 当希望成为常量的值其类型不能被常量声明接受时(类/自定义结构体) —— 静态只读字段

  • 1

数据类型

运算符

语句类型

数组

函数

  • 方法是面向对象范畴的概念,别名“成员函数”,在非面向对象语言中称为函数

  • C# 方法永远都是类(或结构体)的成员,表示类()能做什么事,不可能独立于类()之外,C++ 中函数可以独立于类之外,称为“全局函数”

  • Main 函数为应用程序的主入口点

  • 方法重载、递归,同C++

  • 扩展方法:一种静态方法,但可以像扩展类型上的实例方法一样进行调用;扩展方法向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型

  • 对象初始值

    var team = new BaseballTeam
    {
        ["RF"] = "Mookie Betts",
        [4] = "Jose Altuve",
        ["CF"] = "Mike Trout"
    };

  • 1

按值传参

  • 值类型:简单传递一份复制数据

  • 引用类型:传递的是地址的副本,其存储空间在堆栈和堆中都有,堆栈中存储其在堆中的地址;在函数内部修改数据时,会修改到原对象的数据;如修改对象本身,会创建新对象,不会修改调用处的对象

  • 声明时不带修饰符

  • 1

引用传参

  • 用到 ref、out 关键字

  • 值类型:传递的是其在栈中的地址,方法会根据传递过来的地址去取数据(获得原数据),因此修改的是原值本身

  • 引用类型:传递的是栈中的地址,继而会根据改地址找到对应的堆中的对象数据,因此修改的也是对象本身

    public static void ChangeValue(ref Int32[] k)
    {
        k = new Int32[3] { 3, 3, 3 };
        K[0] = 1;
    }
    Console.WriteLine(...); // 1,3,3

  • 1

引用参数 ref

  • 适用场景:希望将一个已经初始化了的变量以参数的形式带入方法中进行改变,改变后再将值带回来

  • 作用:使得参数通过引用来传递

  • 语法:声明 void Test( ref int b),调用 Test( ref b)

  • 注意:与out的不同之处在于可以双向传值,在传递前一定要对其进行初始化赋值

  • 1

输出形参 out

  • 适用场景:在一个方法中,希望返回多个不同类型的值

  • 作用:使得参数通过引用来传递

  • 语法:声明 void Test( out int b),调用 Test( out a)

  • 注意:方法内必须为 out 参数传递的变量赋值;调用前对参数无需赋值,赋值了也会在方法中被覆盖掉,这是方法内对调用处单向的传值

  • 1

params可变参数

  • 适用场景:方法的参数个数未知

  • 作用:使得个数不确定的可变参数以数组的形式传递

  • 语法:void Test( out int b, params int[] arr)

  • 注意:不允许将params修饰符与refout修饰符组合起来使用,params修饰的参数必须放在最后

  • 1

具名参数

  • 参数的位置不再受参数列表约束

  • 语法:

    static void PrintInfo(string name, int age)
    {
        Console.WriteLine("Hello {0}, you are {1}.",name,age);
    }
    // 调用
    PrintInfo(age: 20, name:"ylt");

  • 1

可选参数

  • 参数因为具有默认值而变得“可选”

  • 1

扩展方法(this 参数)

  • 条件:方法必须 Public static,必须时形参列表第一个,必须由一个 Public static 类统一收纳

  • 扩展方法被定义为静态方法,但它们是通过实例方法语法进行调用的

  • 它们的第一个参数指定该方法作用于哪个类型,并且该参数以 this 修饰符为前缀。 扩展方法当然不能破坏面向对象封装的概念,所以只能是访问所扩展类的public成员

  • 能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型

  • 适用场景:如果同时给多个类添加方法,传统的解决方式是封装一个帮助类,在里面写方法,然后供其他类调用,但调用另一个类的方法,还是有点麻烦,所以我们改写方法为扩展方法,这样调用它的对象可以像调用实例方法一样简便

  • 语法:

    // 比如想给 string 类型增加一个 Add 方法,该方法的作用是给字符串末尾加上字符串s
    public static string Add(this string stringName, string s)
    {
    	return stringName + s;
    }
    
    A a = new A();
    string b;
    b = a.Add(b);

  • 1

成员说明
常量与类关联的常量值
字段类的变量
方法类可执行的计算和操作
属性与读写类的命名属性相关联的操作
索引器与以数组方式索引类的实例相关联的操作
事件可由类生成的通知
运算符类所支持的转换和表达式运算符
构造函数初始化类的实例或类本身所需的操作
析构函数在永久丢弃类的实例之前执行的操作
类型类所声明的嵌套类型
  • 类是一种数据类型,具体到每一个类都是一个自定义类型,可以用类去声明变量,可以用类去创建实例

  • 类是一种“抽象”的数据结构,类本身是“抽象”的结果,例如把学生抽象为 Student 类,也是“抽象”结果的载体,Student 类承载着学生的抽象

分类

  • 静态类 static 只能包含静态成员,不能用 new 关键字实例化

  • abstract 抽象类无法直接实例化,但可以作为提供缺少实现代码的其他类的基类

  • sealed 类可以阻止其他类继承

  • 访问修饰符

    • public 所有类都可以访问该代码

    • private 代码只能在同一类中访问

    • protected 代码可以在同一类中访问,也可以在从该类继承的类中访问

    • internal 代码只能在它自己的程序集中访问,而不能从另一个程序集中访问

  • 泛型类如 List<T>

  • 嵌套类即:类、结构和记录可以嵌套在其他类、结构和记录中

类库的引用
  • 引用 System.Windows.Forms 来实现窗体

  • 1

类的三大成员
  • 属性 Property

    • 存储数据,组合起来表示类或对象当前的状态

    • 属性可以这样定义 public string Name { get; set; }

  • 方法 Method

    • 由 C 语言中的函数(function)进化而来,表示类或对象“能做什么”

    • 工作中 90% 的时间是与方法打交道,因为它是“真正做事”、“构成逻辑”的成员

  • 事件 Event

    • 类或对象通知其它类或对象的机制,为 C# 所特有

    • 善用事件机制非常重要(切勿滥用)

  • 1

分部类
  • 可以在一个代码文件中定义类、结构或方法的一部分,并在其他代码文件中定义另一部分

  • partial 关键字指示可在命名空间中定义该类、结构或接口的其他部分。 所有部分都必须使用 partial ,在编译时,各个部分都必须可用来形成最终的类型。 各个部分必须具有相同的可访问性,如 publicprivate

  • 1

    public partial class Employee
    {
        public void DoWork()
        {
        }
    }
    
    public partial class Employee
    {
        public void GoToLunch()
        {
        }
    }
  • 1

接口、抽象类
  • 具体类——抽象类——接口:越来越抽象,内部实现的东西越来越少

  • 学习设计模式前提:不断的去用,在使用中学习,熟悉接口、抽象类,理解Solid设计原则

  • 抽象类:有abstract 抽象方法或抽象成员,函数成员有至少一个没有被实现

  • 开闭原则:稳定的写进类里,不稳定的写成虚函数让子类实现

  • 1

枚举
  • 1

结构
  • 1

记录
  • record

  • 1

转换类型
  • 1

模式匹配

使用LINQ查询数据

输出

消息框:MesageBox.Show("Hi,");

控制台:Console.WriteLine("Hi");

其它
  • 可以这样理解,有人想找你借钱,他可以直接在你的钱包中拿,这样太不安全,因此必须让别人先要告诉你,你再从自己的钱包把钱拿出来借给别人,这样就安全了

  • invoke方法的初衷是为了解决在某个非某个控件创建的线程中刷新该控件可能会引发异常的问题。举个例子:主线程中存在一个文本控件,在一个子线程中要改变该文本的值,可能会引发异常

    为了避免该问题,需要在子线程中使用invoke方法来封装刷新文本内容的函数,Invoke 或者 BeginInvoke 去调用,两者的区别就是Invoke 会导致工作线程等待,而BeginInvoke 则不会

  • 委托和接口、委托和事件、抽象类和接口、泛型、字段readonly、属性ref out、

  • 构件表格:MyGeneralArgsControl.cs

  • C# 方括号:注解,用于修饰

  • 文件路径用反斜杠\

  • 1

面向对象

封装

继承

多态

结构体

委托与事件

委托

缺点:方法级别的紧耦合,降低可读性,使用不当可能造成内存泄漏和性能下降

  • 概念:委托和类、接口类似的,是一种特殊的类,所以和类一样,也是一种数据结构,也可以声明变量和创建实例,和普通类不同的是委托是“方法模板”,类是“对象模板”

  • 功能:委托是函数指针的“升级版”,C#通过委托这种数据类型保留了C++中与函数指针相对应的功能

  • 函数指针示例:

    // 该指针只能指两个int参数,返回值int的函数
    // 一切皆地址,变量(数据)是一段内存里的值,函数(算法)是一段内存里的一组机器语言指令
    int(*Calc)(int a, int b);
    
    int Add(int a, int b)
    {
        return a + b;
    }
    // 直接调用
    int z = Add(x, y);
    // 间接调用,使用函数指针:
    Calc funcPoint1 = &Add;
    int z = funcPoint1(x, y);

  • 注意:委托一般定义在与类定义平级的部分,用 public 修饰,便于各处调用,也可在 class 内部声明(嵌套声明类);如果某个类想在事件触发的时候收到通知,它必须有一个类型兼容 (返回类型、参数相同) 的方法

  • 简单使用:Action 委托和 Func 委托是 C# 类库内置的委托,Action 委托只能接受返回值void函数,Func委托代表有返回类型的委托

    // 1.泛型 Action 委托只能接受返回值void函数
    // 无参函数
    public void DoNothing() 
    {
    	Console.WriteLine("I had none so I said nothing.");
    }
    // 有参函数
    public void SaySth(int a)
    {
         Console.WriteLine("the num is " + a + ".");
    }
    Action action1 = new Action(DoNothing);
    Action<int> action2 = new Action<int>(SaySth);
    // 直接调用
    DoNothing();
    // 使用委托进行间接调用:
    action1.Invoke(); // 
    action2.Invoke();
    // or 
    action1();
    action2(9);
    
    // 2.泛型 Func 委托,接受有返回类型函数的委托
    public static int add(int a)
    {
        return a + 1;
    }
    // 间接调用
    Func<int,int> func = new Func<int,int>(add);
    int z = func(2);

  • 自定义委托(控制台下使用),语法:

    // 声明一个委托类型,因为该委托用于事件处理,所以以 EventHandler 结尾
    // 注意委托类型的声明和类声明是平级的
    public delegate void Say(int a);
    // 定义具体的方法
    public void SaySth(int a)
    {
        Console.WriteLine("the num is " + a + ".");
    }
    // 定义事件,该事件只和委托Say打交道,事件名come
    public event Say Come;
    // 实例化委托方法1
    Say saysth = new Say(SaySth); // 参数:方法名
    Come += saysth; // 将委托实例加到某个事件上,可加多个,一旦这个事件发生,所有的这些委托实例都会得到通知
    // 实例化委托方法2:直接将方法加到事件上,而省略“委托”的实例化过程,该方法需要与委托格式相同
    Come += SaySth;
    // 触发事件,输出 the num is 6
    Come(6);

  • 自定义委托(窗体中使用),语法:

    // 使用场景:当点击某个“按钮”的时候,就会有一个“Click”事件触发,而这个事件会通知“委托”
    // 窗体应用程序中,“委托”的名字统一使用“EventHandler”
    void EventHandler(object sender, EventArgs e);
    
    // 在设计窗口双击按钮,就会在Form.cs自动生成方法:
    private void button_Click(object sender, EventArgs e) 
    {     
        MessageBox.Show("我被点击了!!!");
    }
    // 同时可以在Form.Designer.cs中看到:
    this.button.Click += new System.EventHandler(this.button_Click); // 这里有一个Click事件,然后将一个委托实例button_Click附加到这个事件上

  • 模板方法(使用Func委托)

    class Program
    {
        static void Main(string[] args)
        {
            var productFactory = new ProductFactory();
    
            // 生产产品
            Func<Product> func1 = new Func<Product>(productFactory.MakePizza);
            Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);
    
            // 通过模板方法 WrapProduct,产出不同的 Box 对象
            var wrapFactory = new WrapFactory();
            Box box1 = wrapFactory.WrapProduct(func1); // Name = "Pizza"
            Box box2 = wrapFactory.WrapProduct(func2); // Name = "Toy Car"
        }
    }
    
    class Product
    {
        public string Name { get; set; }
    }
    
    class Box
    {
        public Product Product { get; set; }
    }
    
    class WrapFactory
    {
        // 模板方法,把方法设定为委托类型,就可以把外部的方法以参数的形式传进这个方法里用
        public Box WrapProduct(Func<Product> getProduct)
        {
            var box = new Box();
            Product product = getProduct.Invoke();
            box.Product = product;
            return box;
        }
    }
    
    // 如果要增加产品,直接加在这里,不用修改其他类
    class ProductFactory
    {
        public Product MakePizza()
        {
            var product = new Product();
            product.Name = "Pizza";
            return product;
        }
    
        public Product MakeToyCar()
        {
            var product = new Product();
            product.Name = "Toy Car";
            return product;
        }
    }

  • 回调方法(使用 Func 和 Action 委托)

    class Program
    {
        static void Main(string[] args)
        {
            var productFactory = new ProductFactory();
            
            // Func 前面是传入参数,最后一个是返回值,所以此处以 Product 为返回值
            Func<Product> func1 = new Func<Product>(productFactory.MakePizza);
            Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);
    
            var wrapFactory = new WrapFactory();
            var logger = new Logger();
            // Action 只有传入参数,所以此处以 Product 为参数
            Action<Product> log = new Action<Product>(logger.Log);
    
            Box box1 = wrapFactory.WrapProduct(func1, log);
            Box box2 = wrapFactory.WrapProduct(func2, log);
        }
    }
    
    // Logger类常用来记录程序运行状态
    class Logger
    {
        public void Log(Product product)
        {
            // Now 是带时区的时间,如果要存储到数据库应该用不带时区的时间 UtcNow
            Console.WriteLine("Product '{0}' created at {1}.Price is {2}", product.Name, DateTime.UtcNow, product.Price);
        }
    }
    
    class Product
    {
        public string Name { get; set; }
        public double Price { get; set; }
    }
    
    class Box
    {
        public Product Product { get; set; }
    }
    
    class WrapFactory
    {
        // 模板方法,提高复用性
        public Box WrapProduct(Func<Product> getProduct, Action<Product> logCallBack)
        {
            var box = new Box();
            Product product = getProduct.Invoke();
    
            // 只 log 价格高于 50 的
            if (product.Price >= 50)
            {
                logCallBack(product);
            }
    
            box.Product = product;
            return box;
        }
    }
    
    class ProductFactory
    {
        public Product MakePizza()
        {
            var product = new Product
            {
                Name = "Pizza",
                Price = 12
            };
            return product;
        }
    
        public Product MakeToyCar()
        {
            var product = new Product
            {
                Name = "Toy Car",
                Price = 100
            };
            return product;
        }
    }

  • 多播委托、隐式异步调用较高级,暂缓学习

    x

  • 适当使用接口替代委托,如果是表示类具有某种能力就用接口

    using System;
    
    namespace DelegateExample
    {
        class Program
        {
            static void Main(string[] args)
            {
                IProductFactory pizzaFactory = new PizzaFactory();
                IProductFactory toyCarFactory = new ToyCarFactory();
                
                var wrapFactory = new WrapFactory();
                Box box1 = wrapFactory.WrapProduct(pizzaFactory);
                Box box2 = wrapFactory.WrapProduct(toyCarFactory);
            }
        }
    
        // 接口的作用就类似一个管道,你可以通过这个接口走过去访问方法
        // 接口的重点在接通,通过接口通向这个方法,委托的重点在于指,我指向这个方法
        interface IProductFactory
        {
            Product Make();
        }
    
        class PizzaFactory : IProductFactory
        {
            public Product Make()
            {
                var product = new Product();
                product.Name = "Pizza";
                return product;
            }
        }
    
        class ToyCarFactory : IProductFactory
        {
            public Product Make()
            {
                var product = new Product();
                product.Name = "Toy Car";
                return product;
            }
        }
    
        class Product
        {
            public string Name { get; set; }
        }
    
        class Box
        {
            public Product Product { get; set; }
        }
    
        class WrapFactory
        {
            public Box WrapProduct(IProductFactory productFactory)
            {
                var box = new Box();
                Product product = productFactory.Make();
                box.Product = product;
                return box;
            }
        }
    }

  • 1

事件

  • 特点:能够 ”发生“ 的什么事情,不会主动发生,一定是被拥有者的内部逻辑触发

  • 功能:通知+可选事件参数;它是使对象或类具备通知能力的成员,用于对象或类之间的动作协调与信息传递,当对象拥有的事件发生时,对象有能力通知别的对象

  • 原理:有一件事——一个或多个人订阅这个事件——事件发生——订阅者收到通知——被通知的人根据拿到的事件信息(事件参数),对事件进行响应(处理事件)

  • 组成部分:拥有者、成员、响应者、处理器、订阅

  • 事件 ≠ 委托类型字段 (≠委托类型),而是委托类型字段的包装器,限制器,从外界只能访问 += -= 操作,让程序更加安全更好维护

  • 程序架构模式:MVC/MVP/MVVM

  • 语法 (3使用最广):

    // 1.(object sender, EventArgs e)
    public event EventHandler OnSelectedEvent;
    // 2.(string a)
    public delegate void SelectedHandler(string a);
    public event SelectedHandler OnSelectedEvent;
    
    OnSelectedEvent += OnSelected;
    public void OnSelected(string code)
    {
        MessageBox.Show("我被选择了!!!");
    }
    // 触发
    OnSelectedEvent?.Invoke(str);
    // #令,多个按钮可以通过多个处理器分别处理,也可以如下写在一起
    
    private void btn_Clicked(object sender, EventArgs e)
    {
        if(sender == this.btn_sayhello)
        {
            this.textBox1.Text = "Hello, World!!!!";
        }
        else if(sender == this.btn_Ok)
        {
            this.textBox1.Text = "Ok";
        }
        else if(sender == this.btn_No)
        {
            this.textBox1.Text = "No";
        }
    }

  • 1

泛型和系统类

窗体

布局

  • 控件布局方式:1可视化,在设计器里拖放,自动计算位置; 2手工布局,代码计算位置; 3布局器,布局其自动布局;

    // 2.手工布局
    // 窗口大小变化时,系统框架自动调用 Layout
    protected override void OnLayout(LayoutEventArgs levent)
    {
        // 调用父类Layout(非必须)
        base.OnLayout(levent);
        
        // 获取窗口大小(不含标题栏)
        int w = this.form.width;
        int h = this.form.height;
        
        // 计算控件位置大小
        this.button.Location = new Point(w - 10, h - 10);
    }

  • Anchor:锚定,固定于某个位置,边距不变 (Top/Right)

  • Dock:停靠,停靠在一侧或中央

  • LayoutEngine布局器:Form和Panel里默认自带一个布局器,也可自定义一个Panel,重写它的LayoutEngine

  • FlowLayoutPanel流式布局:从左到右顺序排

  • TableLayoutPanel表格布局:

  • AfDockLayout暂时没用到:决定四个角由谁占据(上下左右)

  • 1

控件

  • 学习方式:百度+官方文档

  • 文本框TextBox、复选框CheckBox、密码框CheckBox+(PasswordChar=*)、下拉列表ComboBox、列表框ListBox、列表控件ListView、图片框PictureBox(Image、Bitmap)、菜单栏MenuStrip、工具栏ToolStrip、表格控件DataGridView(单元格自定义)、复合控件(继承UserControl)、扩展控件(继承标准控件)、完全自定义(继承Control)

  • 系统对话框:OpenFileDialog打开文件、SaveFileDialog保存文件、FolderBrowserDialog目录选择、ColorDialog颜色选择、FontDialog字体选择

  • 1

右键菜单

  • 用PopupMenu控件能进行右键菜单的实现,它的实现还需要绑定到控件上,并添加右键所需要显示的功能

    // 右键菜单
    private void treeList_PopupMenuShowing(object sender, PopupMenuShowingEventArgs e)
    {
        e.Menu.Items.Clear();
    
        AddRightBarButtonItem(e.Menu, _menuCopy, CopyEventHandler2);
        AddRightBarButtonItem(e.Menu, _menuDelete, DeleteEventHandler2);
        AddRightBarButtonItem(e.Menu, _menuReName, ActRename2);
    
        e.Allow = true;
    }
    
    private void AddRightBarButtonItem(TreeListMenu menu, CustomItem menutem, EventHandler clickEvent)
    {
           DXMenuItem item = new DXMenuItem(); ;
           item.Tag = menutem.TagId;
           item.Caption = menutem.Caption;
           item.Enabled = menutem.Enabled;
           item.Click += clickEvent;
    
           menu.Items.Add(item);
    }

  • 1

C++C#接口

//1.在C#中实现函数
public override App.ErrorStatus getADedSetting(string code, string group, string key, McMunicipalDrainisDeduct value)
{
    //TODO
    return App.ErrorStatus.eUnknownHandle;
}
//不能直接用string的指针,可以重新创建个类携带string:
class McMunicipalDrainisDeduct
{
    public string isDeduct {
    set 
    {
        isDeduct = value;
    } 
    get 
    {
      return isDect;
    } 
  }
}

//2.在C++中定义接口,参数与C#相同
virtual App::ErrorStatus getADedSetting(const std::wstring& code, const std::wstring& group, const std::wstring& key, McMunicipalDrainisDeduct* value) = 0;
//当然也需要定义类McMunicipalDrainisDeduct:
class McMunicipalDrainisDeduct
{
public:
	std::wstring isDeduct = _T("是");
};

//3.在C++中包裹该接口:
//用一类中的函数包裹接口getADedSetting
App::ErrorStatus McMunicipalDrainUiManager::getADedSetting(const std::wstring& code, const std::wstring& group, const std::wstring& key, McMunicipalDrainisDeduct* value)
{
	return McMunicipalDrainGraphics::instance().getUi()->getADedSetting(code, group, key, value);
}

//4.调用
McMunicipalDrainisDeduct value;
McMunicipalDrainUiManager::instance().getADedSetting(component, group, key, &value);

JSON

  • JavaScript Object Notation,一种遵循js对象书写规范的文本格式,只要一串字符串满足了这种格式那么它就是json,可以通过一些api将其转为编程语言内置的类型或者反过来

  • 独立于语言,可用于存储应用程序数据,是轻量级的本地小数据库,比XML小

  • JSON值:字符串、数值、布尔、空、对象、数组

{
    // 大括号表示对象,中括号数组
    "colors":[
        {color:"blue", transparent:0.5},
        {color:"red", transparent:0.3},
        {color:"purple", transparent:0.6}],
    "length":50,
    "right":true
}
  • 1

参考

C#笔记(基础篇)

C# 中out,ref,params参数的使用 (右下角有可爱小人)

C#学习笔记整理【一】

C#语言入门详解 (bilibili搬运刘铁猛老师C#教学视频)

C# 语言入门详解 (yuque.com) (月江流同学看上面视频的笔记)

C# 值类型传参、引用类型传参

C# 中的委托和事件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值