此笔记极水~ ,来自两年前的库存。
是来自 B站 刘铁猛大佬 的视频,因为 好奇学了学。
(引用自:https://www.iteye.com/blog/roomfourteen224-2208838)
上下文:c#中变量的内联赋值其实是在构造函数中完成的,JIT会把变量的赋值语句放入每个构造函数开始的位置,因此,当类中有很多变量使用内联赋值,同时类也有多个构造函数的时候,实际编译生成的代码量会以乘法的方式叠加。比如一个类中有3个变量使用内联赋值,有4个构造函数,那么实际生成的赋值语句的数量将达到3x4=12句。
结论:尽量避免使用内联赋值,优先使用构造函数内赋值
VS 中可以创建两种 C# 程序,无论是 控制台(Console)还是窗口(Form),还是 ASP。都分为 .NET FrameWork 和 .NET Core 两种版本。前者只适用于 Windows,且被微软商业化;后者开源,且支持跨平台。但要论方便 还是 .NET FrameWork 。
=> 依赖关系 ! <-> 软件质量
assembly :程序集,装配件
NuGet:引用包,相比于一个库一个库的引用更快更好。
强依赖:没有这个类无法工作!这个类出错,全都不能用。
UML 通用建模语言,类图。
编程建议:
- 改错一定要找到 根本错误,不要打补丁。
- 程序尽量:
- 类之间低耦合,高内聚(内聚:该放哪放哪)
- 库之间弱依赖,高内聚。
string str = "hello"; int i = 10;
Console.Write($"{str}, {i}");
@"D:\BiShe\TestPic\faces6.jpg"
// \不会被转义
在Qt中,经常使用qDebug()<<“Hello World”;的方式往控制台打印一些输出,以便观察程序的运行情况。
在Java中(eclipse、myeclipse中),经常使用的是System.out.println(“Hello World”);的方式。
在Android中,经常使用的是Log.d(“Hello World”);
. . .
在C#的时候,使用的是Console.WriteLine(“Hello World”);
开发winform的时候,需要先往主函数所在的源文件中加入以下内容。
引入库:
using System.Runtime.InteropServices;
在Main()前,添加:
[DllImport("kernel32.dll")]
public static extern bool AllocConsole();
[DllImport("kernel32.dll")]
static extern bool FreeConsole();
在Main()中,第一行添加:
AllocConsole();
在Main()中,最后一行添加:
FreeConsole();
示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
// 控制台输出,需加入此库
using System.Runtime.InteropServices;
namespace HelloWorld_WindowsForm
{
static class Program
{
[DllImport("kernel32.dll")]
public static extern bool AllocConsole();
[DllImport("kernel32.dll")]
static extern bool FreeConsole();
///
/// 应用程序的主入口点。
///
[STAThread]
static void Main()
{
// 允许调用控制台输出
AllocConsole();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new LoginPage());
// 释放
FreeConsole();
}
}
}
类 —— 现实世界事物抽象所得到的结果
抽象 -> 建模(去伪存真,由表及里)
—— 辩证唯物主义 ~
获取对象 = 实例化 <- 使用 new
操作符。
Form MyForm = new Form();
MyForm.Text = "My Form";
MyForm.ShowDialog();
区别于 Form MyForm;
->该语句未引用任何实例。
多个变量可以引用同一个实例。
个人:C#中变量分为值类型与引用类型,其内存存储方式另有特点,不完全是C/C++,也不完全是Python的形式。 可见 https://www.bilibili.com/video/BV1ma4y1E7LD?p=7,后面记的笔记
类声明中使用成员时,可以this.member
,也可以直接member
特殊类或对象在成员方面侧重点不同。
timer例程:
using System;
using System.Windows;
using System.Windows.Threading;
namespace Timer
{
///
/// MainWindow.xaml 的交互逻辑
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += Timer_Tick;// += 用于挂接事件
timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
//throw new NotImplementedException();
this.timerTextBox.Text = DateTime.Now.ToString();
}
}
}
绑定(Binding)指的是编译器如何把一个成员与类或对象关联起来。
访问成员操作符: .
构成C#语言的基本元素:
@
做前缀。int ,long(字面值后缀 L
)
double(字面值后缀 D
,默认),float(字面值后缀F
)
char(字面值单引号,单字符),string(字面值双引号,多字符)
bool (false/true)
空值null
var 变量类型自动推断
C#中任何对象都可以用
GetType().Name
来获取类型字名符串。single表示单精度浮点型(float)
注意(强类型):var的类型一旦确定,该变量就只能是这个类型。
方法
类内定义,要想类外访问,需要函数顶以前加 public
。
对递归的理解的要点主要在于放弃!
放弃你对于理解和跟踪递归全程的企图,只理解递归两层之间的交接,以及递归终结的条件。
C# 强类型编程语言,比 C/C++强,不会进行类型自动转换!
模仿弱类型的机制:dynamic
反射机制:运行时知道类型的成员。
Type myType = typeof(Form);//类型也是类型
Console.WriteLine(mytype.Name);
//Form
Console.WriteLine(mytype.FullName);
//System.Windows.Forms.Form
Console.WriteLine(mytype.BaseType.FullName);
//System.Windows.Forms.ContainerControl
System.Reflection.PropertyInfo[] pInfo = myType.GetProperties();
foreach (var i in pInfo)
{
Console.WriteLine(i.Name);
}
Console.WriteLine(" ------------- ");
System.Reflection.MethodInfo[] mInfo = myType.GetMethods();
foreach (var i in mInfo)
{
Console.WriteLine(i.Name);
}
存储方式:
C#中是有指针的,但是不能随便用,要用 unsafe
关键字声明 并 在项目 build 属性中指明
public unsafe void func()
{
int* p = stackalloc int[1024];
...
}
或者
public void func()
{
unsafe
{
int* p = stackalloc int[1024];
...
}
}
可利用 windows自带的 performer moniter 检测程序内存 win+r -> 输入 perfmon
类型在C#语言中的作用
一个C#类型中所包含的信息有:
存储此类型变量所需的内存空间大小
此类型的值可表示的最大、最小值范围
此类型所包含的成员(如方法、属性、事件等).此类型由何基类派生而来
程序运行的时候,此类型的变量在分配在内存的什么位置
此类型所允许的操作(运算)
C# 五大数据类型
Object
蓝色字体是真正的数据类型,黑色只是关键字。它们都是关键字,部分类型(蓝色字体)太过常用而被C#吸收为关键字,同时它们又是C#的最基本类型/内建类型。
int,double,short等是结构体?! <- 值类型
.isClass()
:返回数据类型是否是类
int --- Int32
short --- Int16
long --- Int64
char --- 16位
Byte --- 8位
枚举 举例:
Form f = new Form();
f.WindowState = FormWindowState.Normal;//Normal是一枚举类型。
重要:
个人:C#中变量分为值类型与引用类型,其内存存储方式另有特点,不完全是C/C++,也不完全是Python的形式。 可见 https://www.bilibili.com/video/BV1ma4y1E7LD?p=7,后面记的笔记
参数类型前加 ref
引用参数变量
参数类型前加 out
输出参数变量
public double Add(ref double a,ref double b){...}
public void Add(double a,double b,out double c){...}
有效的修饰符组合(opt可选) 类型 变量名 初始化器(opt可选)
变量定义
变量=以变量名所对应的内存地址为起点、以其数据类型所要求的存储空间为长度的一块内存区域
有符号整数的负数用补码的形式存储。
地址从低往高走,存储用高位低位 / 高低字节
类型转换
short s = 1000;
string str = Convert.ToString(s,2);//转换成二进制形式
引用类型声明给 4字节 空白内存
实例化才给内存+地址,以及此时才计算需要多大内存,并把地址放到变量名中
类实例会有默认值,内存统统刷为0.
C# 中本地变量在使用前必须有显式初始化/赋值。
const
装箱
使用 Object
(引用类型)等于一个本地变量(值类型),会在堆上再复制本地变量(值类型)的值并将其地址赋给 Object
(引用类型)
拆箱
将 Object
(引用类型)转换为值类型赋给本地变量(值类型):将堆上内存的值赋给这个本地变量(值类型)
真正的二维数组,而不是 c++ / java 那样的数组的数组。但类似矩阵,要求每行都一样长。
int [,] a = new int [3,4] {
{0, 1, 2, 3} , /* 初始化索引号为 0 的行 */
{4, 5, 6, 7} , /* 初始化索引号为 1 的行 */
{8, 9, 10, 11} /* 初始化索引号为 2 的行 */
};
// 访问数组
a[1,2]
即 数组的数组,内部的数组可以有不同长度 ,java 可以,但 c++ 不行
int [][] a = new int[][]{new int[]{1,2,3},new int[]{4,5} };
//访问
a[1][2]
C# 是纯面向对象的语言,所有方法都是基于类的。
方法命名:
parameter 形式参数
augument 实际参数
直接在 Program类中写函数并调用,需要加static
关键字。
狭义的构造器指 实例构造器
不声明编译器提供默认构造器
类似 C++,有参构造会阻止编译器生成默认构造器
快捷键 ctor + 两次tab键 ,
方法名一样,方法签名不能一样
声明带有重载的方法(类似 C++)
类型形参 << 泛型
参数种类:默认值传递, ref
引用传递,out
输出传递
stack frame
caller 主调者
callee 被调者
压入栈的变量归主调者管,参数从左往右的顺序压入栈
返回的变量存储在 CPU 的寄存器中。
=
是先算等号右边。default(Form)
,注意容易出错!可能并不能用0。->
是指针用的,类似C++,需要 unsafe上下文&
取地址。*x
取对象await
异步 ;初始化器:
Form myForm = new Form(){Text = "Hello"};
myForm.ShowDialog();
基类类型是可以直接用 =
进行初始化
string str = "Hello";
int[] nums = {1,2,3,4,5};
为匿名类型创造对象:
var person = new {Name = "Mr.Okay",Age = 34};
Console.WriteLine(person.GetType().Name);
注意:一旦一个类中使用了 new 操作符时,就会与其他类构成了很强的耦合。
存在一个“依赖注入”的设计模式,可以使变成弱耦合。
var 声明隐式变量
可以在派生类中作为修饰符声明重载基类的方法。
注意其与 override 的区别,new 是隐藏,override是重载,后者可以用于泛型。
检测溢出。引发异常
uint x = uint.MaxValue;
Console.WriteLine(x);
string binString = Convert.ToString(x, 2);
Console.WriteLine(binString);
try
{
uint y = checked(x + 1);
//uint y = unchecked(x + 1);
//uncheck 默认,不检测溢出
Console.WriteLine(y);
}
catch (OverflowException ex)
{
Console.WriteLine("This is Overflow");
//throw;
}
查文档去!
获取结构体类型所占的字节数
获取自定义类型使需要 unsafe
上下文(关键字+设置)。
int[] myIntArray1 = new int[10];
int[] myIntArray2 = new int[]{1,2,3,4,5};
Console.WriteLine(myIntArray2[myIntArray2.Length - 1])
class Person
{
public static operator +(Person p1,Persob p2){...}
}
operator +
中间有个空格
Action act = new Action(Func);
act();// = Func()
强制类型转换 (T)x
隐式(implicit)类型转换
不丢失精度的转换
https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/language-specification/conversions
子类向父类的转换
装箱
显式(explicit)类型转换
有可能丢失精度(甚至发生错误)的转换,即cast(译"铸造")
拆箱
使用Convert类
ToString方法与各数据类型的Parse/TryParse方法
using System;
namespace TypeConvert
{
class Program
{
static void Main(string[] args)
{
Console.Write("Input num1: ");
string str1 = Console.ReadLine();
Console.Write("Input num2: ");
string str2 = Console.ReadLine();
// double num1 = Convert.ToDouble(str1);
// double num2 = Convert.ToDouble(str2);
double num1 = double.Parse(str1);
// Parse 只能转换正确格式的字符串
double num2 = double.Parse(str2);
Console.Write("Result: ");
Console.WriteLine(num1 + num2);
Console.ReadKey(true);
}
}
}
自定义类型转换操作符
//类的自定义转换
using System;
namespace TypeConvert
{
class Program
{
static void Main(string[] args)
{
Stone stone = new Stone();
stone.Age = 5000;
Monkey wukongSun = (Monkey)stone;
Console.WriteLine(wukongSun.Age);
Console.ReadKey(true);
}
}
class Stone
{
public int Age;
}
class Monkey
{
public int Age;
//将 explicit转换成implicit即为隐式转换。
public static explicit operator Monkey(Stone stone)
{
Monkey monkey = new Monkey();
monkey.Age = stone.Age / 500;
return monkey;
}
}
}
浮点除法中,除数可以是 0 .结果是 +/-Infinity
通过double.PositiveInfinity/double.NegativeInfinity
来获取正负无穷大。它们相除得到NaN
注意 左移只补0,右移 正数补0负数补1.
is
是否是某类型(包括父类,一定继承自 object),返回 bool
var res = teacher is Form;
as
判断是否对象是某类型,不是返回 null;是则返回对象引用。可以用于派生类向基类的类型转换
object o = new Teacher();
Teacher t = o as Teacher;
if (t != null)
{
t.Func();
}
正常 int等类型不可赋值为 null
,C#引入可空类型:
Nullable x = null;
Console.WriteLine(x.HasValue);
// false
x = 100;
//等价于
int? x = null;
Console.WriteLine(x.HasValue);
// false
x = 100
其他
int? x = null;
int y =x??0;
// ??:x是null吗?是则y为0.
.Value
属性.HasValue
属性ildasm VS prompt中提供的C#反编译工具。
!分号结束不一定是语句。 using是指令,类中变量声明也不是语句。
if (3>2)
System.Console.WriteLine("Hello");
建议:
一个函数最好只有一个功能
else if
语句是一种逐级筛选。;
了)VS中,使用快捷键:
ctrl + }键
可以在两个大括号之间跳转光标。
配合 goto 等使用。
hello:Console.WriteLine("Hello");
goto hello;
目的:捕获异常,输出易于理解的错误信息,防止程序崩溃。
finally:try后总会被执行
throw:抛出异常,可以指出异常/不指出。
例程,
using System;
namespace Study_Try
{
class Program
{
static void Main(string[] args)
{
Caculator c = new Caculator();
int r = c.Add("abc","0");
Console.WriteLine(r);
System.Console.ReadKey();
}
}
class Caculator
{
public int Add(string str1,string str2)
{
int a = 0;
int b = 0;
bool hasError = false;
try
{
a = int.Parse(str1);
b = int.Parse(str2);
}
catch(ArgumentNullException ane)
{
Console.WriteLine(ane.Message);
hasError = true;
}
catch(FormatException fe)
{
Console.WriteLine(fe.Message);
hasError = true;
}
catch(OverflowException oe)
{
Console.WriteLine(oe.Message);
hasError = true;
}
finally
{
if (hasError)
{
Console.WriteLine("Execution has error!");
}
else
{
Console.WriteLine("Done!");
}
}
int result = a + b;
return result;
}
}
}
建议:平时就养成习惯:一定要尽可能的捕获所有可能出现的异常。异常崩溃是最严重的BUG。
三个都不写,即for(;;)
相当于 while(true)
.
建议:多写算法题,每个算法题做完之后写总结!归类,便于运用/面试时快速想出来。
foreach( var i in nums){...}
IEnumerable
的接口。using System.Collections;
...
int[] intArray = new int[]{1,2,3,4,5,6};
IEnumerator enumerator = intArray.GetEnumerator();//故事-指月
while(enumerator.MoveNext())
{ Console.WriteLine(enumerator.Current);
}
IEnumerator
类的 Reset方法可以重置索引原则:
1.尽早return,在函数中先判断特殊情况是否return。避免头重脚轻,出现if语句中出现大量代码的情况。
- 确保任何情况都可以 return!
什么是字段
字段的声明
字段的初始值
静态字段初始化的时机——类型被加载(load)时
只读字段 ,readonly
关键字
class Student
{
...
static Student()
{
...
}
}
C# 特有,为了替代 C++/JAVA 中 私有变量 + Set/Get成员函数 的组合。
什么是属性
属性( property)是一种用于访问对象或类型的特征的成员,特征反映了状态
属性是字段的自然扩展
属性由Get/Set方法对进化而来
属性是又一个“语法糖"(属性背后的秘密)
属性的声明
完整声明——后台(back)成员变量与访问器(注意使用code snippet和refactor工具)
propfull + 两次tab键
简略声明——只有访问器(查看L代码)
prop + 两次tab键
,set和get方法都是空的,直接分号。
动态计算值的属性
注意实例属性和静态属性属性的名字一定是名词
只读属性——只有getter没有setter(只有get块)
可以在 get/set关键字前加 private声明私有
属性与字段的关系
建议:是动态计算还是存储,根据访问是否频繁决定!
完整声明属性要先有私有字段,再声明访问器。
value
关键字代表传进来的值。stu = new Student(){Name = "Ben"};
using System;
namespace FieldToProperty
{
class Program
{
static void Main(string[] args)
{
try
{
Student student1 = new Student();
student1.Age = 20;
Student student2 = new Student();
student2.Age = 20;
Student student3 = new Student();
student3.Age = 200;
int AvgAge = student1.Age + student2.Age + student3.Age;
AvgAge /= 3;
Console.WriteLine(AvgAge);
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
}
class Student
{
private int age;
public int Age
{
get
{
return this.age;
}
set
{
if (value >=0 && value<=120)
{
this.age = value;
}
else
throw new Exception("value of Age has error");
}
}
}
}
什么是索引器
索引器的声明
需要System.Contains.Generic
名称空间
using System;
using System.Collections.Generic;
namespace Study_Indexer
{
class Program
{
static void Main(string[] args)
{
Student stu = new Student();
stu["Math"] = 100;
Console.WriteLine(stu["Math"]);
Console.ReadKey();
}
}
class Student
{
private Dictionary scoreDictionary = new Dictionary();
public int? this[string subject]
{
get
{
if (this.scoreDictionary.ContainsKey(subject))
{
return this.scoreDictionary[subject];
}
else
{
return null;
}
}
set
{
if (value.HasValue == false)
{
throw new Exception("score has error");
}
if (this.scoreDictionary.ContainsKey(subject))
{
this.scoreDictionary[subject] = value.Value;
}
else
{
this.scoreDictionary.Add(subject,value.Value);
}
}
}
}
}
常量更快,效率更高。其他只是变量
什么是常量
out
关键字。不分配副本,就是原对象。ref
关键字。不分配副本,就是原对象。ref
func(int[] intArray){...}
params
关键字,可以把输出初始化列表直接作为参数调用函数。注:out
、ref
等关键字都是在函数定义与函数调用时都要加在实参/形参前面。
C#中引用代表地址,传入函数的引用类型与实参地址相同,因而操作参数就是改变传入的实参。但是可以 new一个新对象并赋予新地址.
using System;
namespace Study_parameter
{
class Program
{
static void Main(string[] args)
{
Student stu = new Student();
stu.Name = "Mr.Okay";
Console.WriteLine(stu.Name);
Console.WriteLine("ID of old stu is {0}", stu.GetHashCode());
ShowStudentID(stu);
Console.WriteLine("After func , ID of old stu is {0}", stu.GetHashCode());
}
static void ShowStudentID(Student stu)
{
Console.WriteLine("In function , ID of old stu is {0}", stu.GetHashCode());
stu = new Student() {Name = "Ben"};
Console.WriteLine("In function , ID of new stu is {0}",stu.GetHashCode());
}
}
class Student
{
public string Name { get; set; }
}
}
using System;
namespace Study_thisFunc
{
class Program
{
static void Main(string[] args)
{
double pi = 3.14159;
double y = pi.Round(4);
Console.WriteLine(y);
}
}
static class DoubleExtension
{
public static double Round(this double x, int digit)
{
double result = Math.Round(x, digit);
return result;
}
}
}
LINQ:.NET Language Integrated Query:语言集成查询
其实是数据库的本事
建议学SQL!
using System.Linq;
List myList = new List(){1,2,3};
bool res = myList.All(i=>i>10);
它为数组等类型追加了一些扩展方法。
本质是泛型委托,注意这些方法需要的参数,第一个是需要函数传入的类型,之后是需要函数输出的类型。
.select(p=>p.FirstName+""+p.LastName)
,批量获取对象属性/字段.Where(p=>p.FirstName=="Peter")
找出所有姓都是 Peter的人.All(p=>p.FirstName=="Peter")
是否所有人的姓都是 Peter.Any(p=>p.FirstName=="Peter")
是否有人的姓都是 Peter.GroupBy(p=>p.FirstName)
按照姓分组,“组”有Key和Count属性.Count(p=>p.FirstName=="Peter")
一共有多少个姓Peter的C#中所有引用类型都是继承自 Object
对象,都有一个方法:
object.GetHashCode()
委托( delegate )是函数指针的“升级版”,委托是一种“类”!
class Program
{
static void Main(string[] args)
{
Calculator cal = new Calculator();
Action action = new Action(cal.Report);
cal.Report();
action.Invoke();
action();
Func func1 = new Func(cal.add);
Func func2 = new Func(cal.sub);
int x = 100;
int y = 200;
int z = 0;
z = func1.Invoke(x, y);
Console.WriteLine(z);
z = func2(x, y);
Console.WriteLine(z);
}
}
class Calculator
{
public void Report()
{
Console.WriteLine("I have 3 method");
}
public int add(int x,int y)
{
return x + y;
}
public int sub(int x, int y)
{
return x - y;
}
}
委托是一种类,即在类声明的层次声明委托!在名称空间体中声明。
public delegate int Calc(int x, int y);
完整示例
namespace MyDelegate
{
class Program
{
static void Main(string[] args)
{
Calculator cal = new Calculator();
Calc cal1 = new Calc(cal.Add);
Calc cal2 = new Calc(cal.Sub);
Calc cal3 = new Calc(cal.Div);
Calc cal4 = new Calc(cal.Mul);
double x = 1.0;
double y = 0.5;
double z = cal1.Invoke(x, y);
Console.WriteLine(z);
z = cal2.Invoke(x, y);
Console.WriteLine(z);
z = cal3(x, y);
Console.WriteLine(z);
z = cal4(x, y);
Console.WriteLine(z);
}
}
public delegate double Calc(double x, double y);
class Calculator
{
public double Add(double a,double b)
{
return a + b;
}
public double Sub(double a, double b)
{
return a - b;
}
public double Mul(double a, double b)
{
return a * b;
}
public double Div(double a, double b)
{
return a / b;
}
}
}
委托的赋值操作符:+=
using System;
using System.Collections.Generic;
namespace TemplateAndCallback
{
class Program
{
static void Main(string[] args)
{
ProductFactory productFactory = new ProductFactory();
WrapFactory wrapFactory = new WrapFactory();
Func func1 = new Func(productFactory.MakePizza);
Func func2 = new Func(productFactory.MakeToyCar);
Logger logger = new Logger();
Action action = new Action(logger.Log);
Box box1 = wrapFactory.WrapProduct(func1, action);
Box box2 = wrapFactory.WrapProduct(func2, action);
Console.WriteLine(box1.Products.Name);
Console.WriteLine(box2.Products.Name);
}
}
class Logger
{
public void Log(Product product)
{
Console.WriteLine("Profuct '{0}' created at {1}.Price is {2}.",product.Name,DateTime.UtcNow,product.Price);
}
}
class Product
{
public string Name { get; set; }
public int Price { get; set; }
}
class Box
{
public Product Products { get; set; }
}
class WrapFactory
{
public Box WrapProduct(Func getProduct,Action logCallBack)
{
Box box = new Box();
Product product = getProduct.Invoke();
if (product.Price >= 10)
logCallBack(product);
box.Products = product;
return box;
}
}
class ProductFactory
{
public Product MakePizza()
{
Product product = new Product() { Name="Pizza",Price = 15};
return product;
}
public Product MakeToyCar()
{
Product product = new Product() { Name = "ToyCar", Price = 5};
return product;
}
}
}
action1 += action2;
一种类型成员
事件不是委托!
定义:单词Event,译为“事件”
角色:使对象或类具备通知能力的成员
使用:用于对象或类间的动作协调与信息传递(消息推送)
原理:事件模型(event model)中的两个“5”
提示:
事件的功能 = 通知 + 详细信息(可选)
术语统一:
事件模型的五个组成部分
注意
注意事件与事件处理器 的 约定!利用 VS来自动生成约定的东西!
timer.Elapsed += boy.Action;
using System;
using System.Timers;
//省略了名称空间声明
class Program
{
static void Main(string[] args)
{
Timer timer = new Timer();
timer.Interval = 1000; //1s
Boy boy = new Boy();
timer.Elapsed += boy.Action;
timer.Start();
Console.ReadLine();
}
}
class Boy
{
internal void Action(object sender, ElapsedEventArgs e)
{
//throw new NotImplementedException();
Console.WriteLine("Jump!");
}
}
using System;
using System.Windows.Forms;
namespace Study_Event2
{
class Program
{
static void Main(string[] args)
{
Form form = new Form();
Controller ctr = new Controller(form);
form.ShowDialog();
}
}
class Controller
{
private Form form;
public Controller(Form form)
{
if (form!=null)
{
this.form = form;
this.form.Click += this.FormClickDo;
}
}
private void FormClickDo(object sender, EventArgs e)
{
//throw new NotImplementedException();
this.form.Text = DateTime.UtcNow.ToString();
}
}
}
using System;
using System.Windows.Forms;
namespace Study_Event3
{
class Program
{
static void Main(string[] args)
{
MyFrom myForm = new MyFrom();
myForm.Click += myForm.MyDoClick;
myForm.ShowDialog();
}
}
class MyFrom : Form
{
internal void MyDoClick(object sender, EventArgs e)
{
this.Text = DateTime.UtcNow.ToString();
}
}
}
事件的声明
有了委托字段/属性,为什么还需要事件?
所以事件的本质是委托字段的一个包装器
用于声明事件的委托类型的命名约定
事件的命名约定
//事件参数类声明
//注 1.要继承自 EventArgs
//注 5.命名习惯:以 EventArgs 结尾
public class OrderEventArgs:EventArgs
{
public string DishName { get; set; }
public string Size { get; set; }
}
// 事件处理器声明
//注 1.它其实是个委托
//注 2.委托是个类,与类声明同级,都在名称空间中
//注 3.注意参数列表:事件拥有者,事件参数
//注 4.返回值为 void
//注 5. 命名习惯:以 EventHandler 结尾
public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
// 事件声明(在事件拥有者类的定义中)
//注 1.两部分:私有委托字段 + 事件完整声明
private OrderEventHandler orderEventHandler;
public event OrderEventHandler Order
{
add
{
this.orderEventHandler += value;
}
remove
{
this.orderEventHandler -= value;
}
}
//事件触发
if(this.orderEventHandler != null)
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = "GongPaoChicken";
e.Size = "large";
this.orderEventHandler.Invoke(this, e);
}
using System;
using System.Threading;
namespace Study_Event5
{
class Program
{
static void Main(string[] args)
{
Customer customer = new Customer();
Waiter waiter = new Waiter();
customer.Order += waiter.Action;
customer.Action();
customer.PayTheBill();
}
}
public class OrderEventArgs:EventArgs
{
public string DishName { get; set; }
public string Size { get; set; }
}
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 double Bill { get; set; }
public void PayTheBill()
{
Console.WriteLine("I will pay {0}.", this.Bill);
}
public void WalkIn()
{
Console.WriteLine("Walking into the restaurant");
}
public void SitDown()
{
Console.WriteLine("Sitting down ...");
}
public void Think()
{
for(int i=0;i<5;i++)
{
Console.WriteLine("Let me think ...");
Thread.Sleep(1000);
}
if(this.orderEventHandler != null)
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = "GongPaoChicken";
e.Size = "large";
this.orderEventHandler.Invoke(this, e);
}
}
public void Action()
{
Console.ReadLine(); ;
this.WalkIn();
this.SitDown();
this.Think();
}
}
public class Waiter
{
internal void Action(Customer customer, OrderEventArgs e)
{
Console.WriteLine("I will serve you the dish: {0}.",e.DishName);
double price = 10.0;
switch(e.Size)
{
case "small":
price *= 0.5;
break;
case "medium":
price *= 1.0;
break;
case "large":
price *= 1.5;
break;
default:
break;
}
customer.Bill += price;
}
}
}
//事件声明
public event OrderEventHandler Order;
//事件触发
if (this.Order != null)
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = "GongPaoChicken";
e.Size = "large";
this.Order.Invoke(this, e);
}
EventHandler
.事件真的是“以特殊方式声明的委托字段/实例"吗?
为什么要使用委托类型来声明事件?
对比事件与属性
~Student()
{
...
}
class Student
{
public static int Amount{get;set;}
static Student()
{
Amount = 0;
}
public Student()
{
Amount++;
}
}
Type t = typedef(Student);
object o = Activator.CreateInstance(t,42,"Ben");
Student stu = o as Student;
Console.WriteLine(stu.Name);
Type t = typedef(Student);
dynamic d = Activator.CreateInstance(t,42,"Ben");
Console.WriteLine(d.Name);
class Student
{
class aabb
{
...
}
...
}
C#/JAVA :声明即定义。
public
表示可以被其他项目的程序通过依赖+名称空间所见
internal(在名称空间中是默认)
表示不可以被其他项目(Assembly)的程序所见,但可以被同项目不同名称空间的程序所见
注:一个项目编译结果是一个程序集/装配集(Assembly)
一个名称空间中如果没有任何类暴露,则名称空间也不会暴露。
_foo
,即在前加一个下划线,表示是私有的实例字段sealed
,则不可再被继承横向扩展:增加成员
纵向扩展:重写
构造的调用顺序:同C++,从基类到派生类
关键字 base
,派生类可以以此访问上一层一类的对象。但是也只能访问上一级,不能 base.base
!!!构造器是不会被继承的!!!只会有编译器提供的默认构造器(前提还是基类可以默认构造)
C#也有初始化列表,但只能是this / base
class Vehicle
{
public string Owner { get; set; }
public Vehicle(string owner)
{
Owner = owner;
}
public Vehicle():this("N/A"){}
}
class Car:Vehicle
{
public Car():base("N/A")
{
}
}
多态,基类可以引用派生类对象,并调用派生类重写的方法。
class Vehicle
{
public virtual void Run()
{
Console.WriteLine("I'm running. ---In Vehicle");
}
}
class Car:Vehicle
{
public override void Run()
{
Console.WriteLine("I'm running. ---In Car");
}
}
对于如下代码:
Vehicle vc = new Car();
vc.Run();
输出
I'm running. ---In Car
几乎不用!
如果不用重写,也就是不用 virtual
和 override
,则不会产生多态的效果。声明类型是啥就用哪个版本
可选:用于隐藏的函数可以加 new
关键字。
class Vehicle
{
public virtual void Run()
{
Console.WriteLine("I'm running. ---In Vehicle");
}
}
class Car:Vehicle
{
public override void Run()
{
Console.WriteLine("I'm running. ---In Car");
}
}
对于如下代码:
Vehicle vc = new Car();
vc.Run();
输出
I'm running. ---In Vehicle
为做基类而生的“抽象类”与“开放/关闭原则”
“开放/关闭原则”:除非修Bug和添加功能,否则不应该修改一个类的代码!
有未实现的函数成员,即被 abstract
修饰,且不是private。此时这个类就是抽象类,其类声明前要加关键字abstract
。
abstract class Vehicle
{
...
public abstract void Run();
}
class Car:Vehicle
{
...
public override void Run(){...}
}
其派生类需要实现这些函数成员才能摆脱抽象类的身份。注意,派生类在实现时也需要在前面加关键字override
.
一个抽象类中全都是 abstract
的函数,则可把abstract class
,直接替换为 关键字interface
—— 接口,此时需要再把类中函数声明前的public abstract
,以及派生类中的override
也全部去掉!
所以接口都是 public的!是要提供的一种服务!
interface IVehicleBase
{
...
void Run();
void Stop();
}
abstract class Vehicle:IVehicleBase
{
...
public abstract void Run();
public void Stop(){...}
}
什么是接口和抽象类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tu50CRUW-1692105515282)(C:%5CUsers%5CLiyi%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1626612001693.png)]
接口设置要合理,调用者绝不多要!即不能设计不会被使用的接口。
即也是一种单一职责原则。
做法:把功能本质不同的接口分开
注意:把握一个度,也不能切的太碎!
interface IGentleman
{
void Love();
}
interface IKiller
{
void Kill();
}
class WarmKiller:IGentleman,IKiller
{
public void Love(){...}
//↓ 只有我们将这个实例作为IKiller,时才会看见Kill接口!
void IKiller.Kill(){...}
}
...
var wk = new WarmKiller();
//wk 看不到Kill方法
IKiller killer = wk;
//killer 此时就能看见Kill,但看不见Love
//or
IKiller killer = new WarmKiller();
var wk1 = killer as WarmKiller;
//wk1 看不到Kill方法;能看见Love
interface ITank:IVehicle,IWeapon{}
…
NuGet包管理器:Moq —— 帮助测试省去重复定义类的过程
.NET框架的功能,并不是语言的功能!.NET Framework与Core也不同,但是API不同
反射:reflect
反射与依赖注入
//直接使用
Student stu = new Student();
var t = stu.GetType();//获取类型信息
object o = Activator.CreateInstance(t);
MethodInfo studyMi = t.GetMethod("study");
studyMi.Invoke(o,null);
NuGet中找 DependencyInjection。微软的
解决类型膨胀,成员膨胀的问题。
class Program
{
static void Main(string[] args)
{
Apple apple = new Apple() { Color = "Red" };
Book book = new Book() {Name = "MyBook"};
Box box1 = new Box(apple);
Box box2 = new Box(book);
Console.WriteLine(box1.Cargo.Color);
Console.WriteLine(box2.Cargo.Name);
}
}
class Apple
{
public string Color { get; set; }
}
class Book
{
public string Name { get; set; }
}
class Box
{
public TCargo Cargo { get; set; }
public Box(TCargo Cargo)
{
this.Cargo = Cargo;
}
}
interface IUnique
{
TId ID{get;set;}
}
class Student:IUnique
{
public TId ID {get;set;}
public string Name {get;set;}
}
//另一种方式
class Student:IUnique
{
public ulong ID {get;set;}
public string Name {get;set;}
}
System.Collections.Generic
C#提供:
还可以根据输入的类型找到正确的重载函数
Func func1 = (double a, double b)=>{return a+b;}
//因为委托中已经确定输入类型了,所以此时Lambda的输入类型可以去掉
枚举类型
结构体(struct)