架构技术交流 2019-10-28
下文图片出自C#与.NET+4高级程序设计+第5版与百度
.NET理解为一个运行库环境和一个全面的基础类库。
.NET三个关键实体(构造块):CLR、 CTS、 CLS
公共语言运行库层为CLR 。功能:定位加载和管理.NET类型。也负责底层的工作如内存管理,处理线程等等。
公共类型系统:CTS 。 描述了运行库所支持所有可能的数据类型和编程结构。
公共语言规范:CLS。定义所有.NET都支持的公用类型和编程结构的子集。
Reflector查看程序集,反编译。
.Net
调用JS函数<script language=”javascript” type=”text/javascript”> </script>
表单数据传输有两种方式:POST 和GET
POST方式,表单数据对于外部世界来说不直接可见,没有字符长度限制
GET方式,表单数据会以&分隔。字符受限。
根据最初的HTTP请求创建了程序集,它就将被所有后续的请求重用,不必再次编译。同一页面的后续请求会快很多。
调试跟踪ASP.NET页面 通过将<%@page%>指令中的Trace特性设置为true来启用跟踪支持。
通过Trace.Write()记录日志。
Javascript运行机制
JavaScript一大特点是单线程,同一时间只能做一件事情。
JavaScript将任务分为两种,一种是同步任务,一种是异步任务。
同步:排队执行任务
异步:通过任务队列通知主线程,某个异步可以执行了才会进入主线程执行。
异步运行机制:所有的任务都在主线程上执行,形成一个执行栈。主线程之外存在一个“任务队列”(异步任务产出了结果,就在任务队列中放入一个事件),执行栈中的任务执行完毕就从任务队列中调取任务,看里面有哪些事件,把异步任务进行执行栈,开始执行。
定时器:setTimeout与 setInterval
setTimeout(function() { {console.log(i); }, 1000);第二参数表示每隔多少毫秒执行。
三层架构
三层架构分为UI(表现层),BLL(业务逻辑层) DAL(数据访问层)
使用三层架构的目的是解耦。 任何一层发生变化都不会影响到另外一层。结构清晰,可维护性高,有利于同步开发。
如何获取web.config的<connecttionString>的连接字符串
如果name=conString
那么通过代码string connectionString;
connectionString = ConfigurationSettings.AppSettings["conString"];可以获取到conString里面的字符串内容。
今天开会提到的存储过程去查询了一下
存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。
存储过程就类似是一个方法并可能带有参数,可以调用。
C#的+符号被编译器处理为String.Concat()调用。
字符串是不可变的。
String类型的方法其实返回了一个按修改格式的新字符串对象。
在调用了大写之后,String字符串依旧没有变换。
静态方法可以直接被调用,无需创建类的实例。
成员重载:当定义一组名字相同的成员(它们的参数数量或者类型不同时),这样的成员被叫做重载。
枚举
在创建系统的时候,创建一组符号名来对应已知的数字值会很方便(符号代替数字)。 例如:
Enum EmpType
{
Manager,//默认情况从0开始, 也可以自己赋值 但也不可以不连续
Grunt,//1
Contractor,//2
VicePresident,//3
}
使用枚举
在上面的枚举中,必须以枚举名(EmpType)来设置值。
设置Grunt
根据枚举名 EmpType emp=EmpType.Grunt; emp的值就是Grunt。
五大数据类型:类 结构体 枚举 接口 委托
值类型(放在栈,基类是ValueType,分配一个值)和引用类型(放在堆,基类除了ValueType都可以。指向一个内存位置)
值类型在越出定义的作用域时消失,引用类型当堆被垃圾回收时。
值类型赋值给另一个的时候,就是对字段成员进行复制。
引用类型是在内存中重定向引用变量的指向。
C#中定义的值类型包括原类型(Sbyte、Byte、Short、Ushort、Int、Uint、Long、Ulong、Char、Float、Double、Bool、Decimal)、枚举(enum)、结构(struct)
引用类型包括:对象、类、数组、接口、委托、字符串(String)等。
Ref和out有什么区别?
Out输出参数不需要初始化,Ref引用参数必须初始化
重载(方法名相同,返回类型和参数不同)和重写的区别?
Ref返回多个值。
P2指向的P1的内存。 当P1改变 P2也一样改变。
C#可空类型 null用来建立一个空的对象引用,值类型不能被赋空。
??操作符。 获得的值实际上是null时,我们可以用??给一个可空类型赋值。
默认构造函数不会接受任何参数,例如
Public person(){
Weight = 10;
Height=10;
}
充当初始化的功能。
构造函数也可以自定义
Public person(int wei)
{
Weight=wei;
}
没有赋值就是默认值0;
总结:构造函数的不同就是参数个数和类型的不同。构造函数没有返回类型 每个class类都有默认的构造函数,但是一旦定义了自定义构造函数,默认的会自动移除。
This关键字
解决传入参数名字与数据类型相同时产生作用域歧义。
当变量名与成员变量类型名字相同时,作用域会指向变量而不是字段
加入this 消除这种歧义。
This关键字还有另一个用法。例如:
对构造函数某个参数需要做卡关,验证。
Class Motocycle{
Int goodspeed;
Int drivername;
//每次调用构造函数就调用这个方法
Public Motocycle(int speed)
{
Setgoodspeed(speed);
}
Public void Setgoodspeed(int speed)
{
If(speed>10)
{
speed= 10;
}
Goodspeed=speed;}}
使用This关键词可以串联构造函数:结构如下
静态static
静态字段是所有对象共享的,如果需要共享数据可以使用静态成员。
关于静态构造函数
它不可以被重载,静态构造函数只会执行一次,执行优先级最高。
静态类
使用private关键字将默认构造函数定义私有防止创建实例。
使得对象在内存中只存在一个 可参考代码。
public class SimpLeMath { private SimpLeMath() { } private static SimpLeMath simple { get; set; } public static SimpLeMath GetSimpLeMath(){ if(simple==null){ simple=new SimpLeMath(); return simple; }else{ return simple; } } }
按照以上代码 构造函数私有时,实例不可以创建只有调用自身的GetSimpleMath()方法。
静态类不可以new实例,功能公开。
OOP支柱
3 个核心:封装 继承 多态
封装就是将实现细节隐藏起来,也起到了数据保护的作用。
继承就是基于已有类来创建新类可以继承基类的核心功能。
另外一种代码重用是:包含/委托,这种重用不是父子类的继承关系,而是一个类定义为另一个类的成员变量。
一个简单的例子,汽车和收音机,汽车里面有一个收音机,不能说继承,不符合逻辑,只能说是一种对象包含了另一个对象。汽车对象中调用了收音机对象中的方法。
多态
同一种方式处理相关对象的能力。
相同的请求操作不同。
两个原则:里氏替换原则:子类对象能够替换其基类对象被使用。
开放封闭原则:对外扩展开放,对内修改封闭。
定义一个抽象方法,派生类重写抽象方法具体实现。
Aniaml an=new Cat();
通过创建an 就可以指向多个派生类对象。
an= new dog();
an=new chicken();
创建cat对象,基类指向子类
多态的实现:
访问修饰符记下三个常用的
Public公共的没有限制
Private私有的 本类可用
Protected 本类和子类使用
默认修饰符都是私有的。
Set get方法就是最基础的封装。
.NET语言提倡使用属性来强制封装状态数据。属性会总是映射到“实际的”访问方法和修改方法。创建了属性会在背后调用相应的get 和set来封装。
我查了一下封装的意义
还有一点是
你可以控制它的功能
使用类的属性(不太清楚含义)
属性内部表示
如果已经写了封装set get方法又重新定义了set get方法那么编译会出错,基础类库会使用类型属性而不是传统的访问和修改方法。
Class employee { Public static companyName{get;set;} 简单写法 } Class employee { Private static string companyName;//对字段封装 Public staric string company//封装没有括号() { Get{return companyName;} Set(companyName=value;) } } Main(){ Employee.company=”My company”; }
自动属性
比较方便。但自动属性必修同时支持读写功能。
Const用来定义常亮
比如我定义一个π值
Public const double pai=3.14;
Ps:关键字都是加字数据类型前面
Readonly只读字段 和常量相似 不能在赋初始值后改变。
但和常量也有不同的地方。
赋给只读字段的值可以在运行时决定,因此在构造函数中赋值是合法的。例如:
Class{
Public readonly double pi;
Public class(){
Pi=3.14;
}
}
继承与多态
在C#中定义冒号:操作类之间的关系。
例如:MiVan继承Car类
Class MiVan:Car
{
}
MiVan可以调用父类的公共属性,继承保护了封装,无法访问私有成员。
.NET不允许多重继承
可以将类、结构或接口的定义拆分到两个或多个源文件中,在类声明前添加partial关键字即可。
Sealed关键字
防止发生继承
创建子类对象访问基类功能,并扩展。
Base关键字
静态成员只能由类来访问,不能由对象来访问。
Base和this都是访问类的实例
一般基类的默认构造函数会被自动调用
上述例子,显示调用合适的自定义基类构造函数。解决只读问题和低效性。增加了自定义的构造函数,就把默认的构造函数自动移除了,所以得重新定义默认构造函数。
基类加Protected关键字
好处:派生类就可以直接访问基类的信息。
坏处:有可能绕过公共属性设置的业务规则。
受保护的数据被认为是私有的。
包含/委托编程
Has-a关系
简单的说,委托就是增加公共成员到包含类,以便使用被包含对象的功能。
非嵌套类不能使用private关键字声明。
Public class OuterClass
{
//公共嵌套类型可以被任何人使用
Public class PublicInnerClass()
//私有嵌套类型只可以被包含类的成员使用
Private class PrivateInnerClass()
}
C#多态
相同的请求做出不同的响应。
如果基类希望定义由子类重写的方法,必须加上virtual关键字。
如果子类希望改变虚方法的实现细节,必须使用override关键字。
可以通过base关键来使用默认行为
例如:
Base.GiveBonus();
使用了基类的默认行为。
抽象类
防止创建模糊的基类,使用abstract关键字。
Abstract class Employee
{
}
构建多态接口
抽象方法只可以在抽象类中
dynamic 类似于javascript中的Var 弱类型(与底层打交道会用的到)
接口
接口是一组抽象成员的集合。
C# 用interface来定义接口。
接口中不能有字段,也不能有构造函数,接口不能提供实现。
接口可以实现多继承
as关键字判断是否支持一个接口 null
is关键字判断是否实现一个接口 false
接口在以下情况特别有用:
只有一个层次结构,但是只有一部分派生类支持某个公共行为
需要构建的公共行为跨多个层次。目前没有理解
接口还没有具体的写过(理解还不深)
例如:接口类ICamera
VirtualCamera 继承ICamera();
ICamera camera=new VirtualCamera();
调用camera的方法。
泛型
泛型是个容器
装箱 将值类型存放在栈上的 转换成存在堆上的引用类型
拆箱 反过来
与非泛型容器相比,提供更好的性能,不会导致装箱或拆箱的损耗
泛型更类型安全,因为只包含我们指定的类型
泛型有多种 Queue stack 等泛型类型 。还有种可以自动排序的SortedSet泛型。
委托
本质上讲 委托是一个类型安全的对象,它指向程序中另一个以后会被调用的方法。通俗的讲就是传方法。
包含3点
1 它所调用的方法的名称
2 该方法的参数
3 该方法的返回值
关键字为delegate
下面给个委托的简单例子:
public delegate int BinaryOp(int x, int y); public class SimpLeMath { public int Add(int x, int y) { return x + y; } public int Subtract(int x, int y) { return x - y; } } class Program { static void Main(string[] args) { Console.WriteLine("************delegate************"); SimpLeMath a = new SimpLeMath(); BinaryOp b = a.Subtract;//给委托一个方法名 Console.WriteLine("10+10 is {0}", b(10, 10)); Console.ReadLine(); } }
委托BinaryOp b指向方法Subtract 这样b就代替了方法Subtract。
具体的实例 :https://www.cnblogs.com/Alex-bg/archive/2012/05/13/2498043.html 参考这篇文章
委托还可以多路广播,一个委托对象可以维护一个调用方法的列表。使用+=操作符。按照方法列表的顺序执行方法。-=用来移除方法。
委托协变
通过委托实现继承的感觉,类似于里氏替换原则。
线程与进程
进程和线程放在一起谈比较好理解。
进程可理解为一个应用程序。一个进程中只有一个任务,就是单线程,有多个任务就是多线程。
下面附上一个线程的小例子:
//打印 public class Printer { public void PrintNumbers() { Console.WriteLine("->{0} is executing PrintNumbers()", Thread.CurrentThread.Name); Console.Write("Your numbers: "); for (int i = 0; i < 5; i++) { Console.Write("{0}", i); Thread.Sleep(2000);//间隔2秒 } } } class Program { static void Main(string[] args) { Printer print = new Printer(); ThreadStart thread = print.PrintNumbers;//ThreadStart委托 Thread t = new Thread(thread); t.IsBackground = true;//默认为前台线程,不会随Main()方法的结束而结束。可以设置thread.IsBackground为True,则转为后台线程,即可随Main()方法的结束而结束。 t.Start(); //Thread t = new Thread(new ThreadStart(print.PrintNumbers)); //t.Name = "Thread"; //t.Priority = ThreadPriority.Highest; //t.Start(); //第一种写的清楚点,第二种写的简易。 MessageBox.Show("************test************"); } }
开启一个线程运行Print操作 同时也显示了提示信息。如果不用线程则将等Print结束后才可以显示提示信息。
前后台线程在进行解释:前台线程可以阻止应用程序的终结,要一直等到所有前台线程结束方可终结,而后台线程当应用程序终结时,所有的后台线程也自动终止。
创建10个线程
Thread [] threads= new Thread[10]; for(int i=0;i<10;i++) { threads[i]=new Thread(new ThreadStart(XX方法名)); }
并发问题
多线程共同使用某个共享资源的时候,会造成冲突。
举个生活例子以及解救方法
生活中我们会遇到这样的情况:去商场买衣服时,如果想要试一试是否合身,一般可以利用商场提供的试衣间,但一个试衣间一个时间段内只能有一个人在试衣。如果有多个人都要使用同一个试衣间(共享资源),而试衣间的门又没有加锁,则会存在共享资源的冲突问题。 实际生活中,这个问题是这样解决的: 现在假设有A、B、C同时要使用试衣间,A进去后,将门锁上,B看到门已上锁,则知道有人在试衣,会进行等待,A使用完试衣间后,打开锁(解除对共享资源的锁定),则B知道此时试衣间可以使用,则B使用试衣间。C同样。 简单的一把锁,解决了试衣间的使用冲突问题。
在代码中使用lock关键字来进行锁定。
public class Printer { private object threadLock = new object(); public void PrintNumbers() { lock (threadLock)//所有的代码都必须在锁定范围中 { Console.WriteLine("->{0} is executing PrintNumbers()", Thread.CurrentThread.Name); Console.Write("Your numbers: "); for (int i = 0; i < 5; i++) { Console.Write("{0}", i); Thread.Sleep(2000); } } } }