1.表连接的SQL语句,对于内连接 inner join来说,join语句和把条件写在where语句里结果一样,但是对于左连接和右连接就不一样了,例如左连接,on后面的条件只对右表有效,对左表的筛选就要写在where里面。而且join on是先把两张表笛卡儿积组成一张表,再用where条件筛选,对于join很多表来说,从左往右,左边的表越小效率越高。所以尽量把条件写在on里面可以提高效率。
2.用Dapper框架的时候,尽量不要用拼接字符串的形式组SQL,因为有被攻击或者SQL注入的风险,尽量用StringBulder。
Dapper.Query() 返回查询结果 参数一般为 SQL语句,参数变量,事务,SQL类型(SQL语句或者存储过程)
Dapper.Execute() 返回受影响的行数
GET请求和POST请求
1.GET和POST是http协议的两种请求方式,GET一般用来请求数据,POST一般用来传递参数用于更新等。
2.GET请求会把参数带在URL,对用户是可见的,而POST是把参数放到请求体中,但GET也可以把参数放到请求体中,但是服务器不一定能接收到
3.POST请求相对于GET请求更安全。因为GET请求传递的参数会显示在url中,用户在访问的时候浏览器会缓存网页,这样别人在查看浏览器浏览记录的时候会获取私人信息,POST请求不会被用户看到,所以安全性高。
4.GET请求在URL中传送的参数是有长度限制的,而POST么有。
5.对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
6.对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。但并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。
EntityFrameWork 和 .NET Core对比
不能说Core一定比EF好,但是Core有一个最大的优点是跨平台,开源,而且比较多的IDE支持Core(VS ,VS CODE),而EF相对来说更加稳定,EF应用也更广泛,另外WCF服务和窗体不支持Core
但是当客户有以下需求时我们更应该考虑Core
- 用户有跨平台需求。
- 用户正在面向微服务(Azure)。
- 用户正在使用 Docker 容器。
- 需要高性能和可扩展的系统。
- 需按应用程序提供并行的 .NET 版本(不同版本的.net core(可能是2.0 和3.0),像EF可能就是4.0 或者 4.1)。
3.String和StringBuilder
string本身是不可改变的,它只能赋值一次,每一次内容发生改变,都会生成一个新的对象,然后原有的对象引用新的对象,而每一次生成新对象都会对系统性能产生影响,这会降低.NET编译器的工作效率
而StringBuilder类则不同,每次操作都是对自身对象进行操作,而不是生成新的对象,其所占空间会随着内容的增加而扩充
当程序中需要大量的对某个字符串进行操作时,应该考虑应用StringBuilder类处理该字符串,其设计目的就是针对大量string操作的一种改进办法,避免产生太多的临时对象;而当程序中只是对某个字符串进行一次或几次操作时,采用string类即可。
4.ASP.NET和.NET的区别:
.Net是平台,下面包括很多种语言,比如C#,VB,C++等,而且.NET有主要分为两部分,WinForm(CS)和WebForm(BS),而ASP.NET属于.NET下的一部分,主要用来做WebForm项目。编译后形成CLR(中间语言),然后通过服务器的IIS+.Net FrameWork再次编译来运行。
5.委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
用法可以参考这篇博客 https://www.cnblogs.com/yinqixin/p/5056307.html
6.装箱与拆箱
装箱就是隐式的将一个值型转换为引用型对象。
拆箱就是将一个引用型对象转换成任意值型。
比如:
int i=0;
Syste.Object obj=i;
这个过程就是装箱!就是将 i 装箱!
比如:
int i=0;
System.Object obj=i;
int j=(int)obj;
这个过程前2句是将 i 装箱,后一句是将 obj 拆箱!
7.BS和CS架构的优缺点
B/S 浏览器端/服务端 : 开发成本低,版本永远是最新的,适用范围广,对安全的控制能力相对弱, 面向是不可知的用户群.
C/S 客户端/服务端:通讯成本低,需要安装客户端,手动升级
8.DataReader和DataSet
两者最大的区别在于,DataReader使用时始终占用SqlConnection,在线操作数据库.任何对SqlConnection的操作都会引发DataReader的异常.因为DataReader每次只在内存中加载一条数据,所以占用的内存是很小的..因为DataReader的特殊性和高性能.所以DataReader是只进的.你读了第一条后就不能再去读取第一条了.
DataSet为离线操作数据,DataSet会将数据一次性读入内存,然后断开连接,这时其它操作就可以使用SqlConnection连接对象。因为DataSet将数据全部加载在内存中.所以比较消耗内存.但是确比DataReader要灵活.可以动态的添加行,列,数据.对数据库进行回传更新操作
9.在c#中using和new这两个关键字有什么意义?using 指令和语句 new 创建实例 new 隐藏基类中方法。
using 引入名称空间或者使用非托管资源,相当于 try catch finally 使用完对象后自动执行实现IDisposable接口的类的Dispose方法释放对象资源
new 新建实例或者修饰一个方法,表示此方法完全重写(即隐藏基类方法)
10. 虚函数 / 抽象函数 / 接口 / 静态函数
①虚函数:有实现体,实现多态性,子类可以重写也可以不重写直接拿来用。用Virtual 修饰,只能被单继承
②抽象函数:是一个类,有构造函数,用abstract修饰,没有实现体,非虚子类必须重写,有抽象方法一定是抽象类,但抽象类不一定有抽象方法。只能被单继承
③接口:不是类,没有构造函数,支持多态,用 Interface修饰,没有实现体,子类必须重写,支持多继承
④静态函数:用static修饰,第一次使用时被初始化,对于该类的所有对象来说,static成员变量只有一份,static方法中不可访问非static的成员。
类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。
11.重载与重写
重载:同一方法名,参数不同,或者参数个数不同,是多太的一种体现,或者是返回值的不同,或者是修饰符不同
重写:继承父类,对父类的方法重写
override(重写)
1、方法名、参数、返回值相同。
2、子类方法不能缩小父类方法的访问权限。
3、子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。
4、存在于父类和子类之间。
5、方法被定义为final不能被重写。
overload(重载)
1、参数类型、个数、顺序至少有一个不相同。
2、不能重载只有返回值不同的方法名。
3、存在于父类和子类、同类中。
12.类和结构有以下几个基本的不同点:
类是引用类型,结构是值类型。
结构不支持继承。
结构不能声明默认的构造函数。
类的对象是存储在堆空间中,结构存储在栈中。堆空间大,但访问速度较慢,栈空间小,访问速度相对更快
13.值类型和引用类型
值类型(如 char、int 和 float)、枚举类型和结构类型。
引用类型包括类 (Class) 类型、接口类型、委托类型和数组类型。
声明一个值类型变量,编译器会在栈上分配一个空间,这个空间对应着该值类型变量,空间里存储的就是该变量的值。
引用类型的实例分配在堆上,新对应的是栈中存储该值的内存分配地址
14.反射(Reflection)
优点:
1、反射提高了程序的灵活性和扩展性。
2、降低耦合性,提高自适应能力。
3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。
缺点:
1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。
反射(Reflection)的用途
反射(Reflection)有下列用途:
它允许在运行时查看特性(attribute)信息。
它允许审查集合中的各种类型,以及实例化这些类型。
它允许延迟绑定的方法和属性(property)。
它允许在运行时创建新类型,然后使用这些类型执行一些任务。
C# 高级教程 https://www.runoob.com/csharp/csharp-attribute.html
15.泛型
当我们写一个带参的方法时,参数类型一般需要指定,Public void Test(int a)
但是类似于重方法载,会传入不同的参数类型时,我们可能会Public void Test(string b),Public void Test(datetime c),但是如果类型真的很多,这种方法显然不合适。
或者我们可以 Public void Test(Object a) 看起来一劳永逸,但实际上这个过程需要大量的装箱拆箱操作,如果访问量很大是十分消耗性能的。这个时候就可以用泛型 Public void Stack<T>
16.匿名方法:
在匿名方法中您不需要指定返回类型,它是从方法主体内的 return 语句推断的。
匿名方法是没有名称只有主体的方法。
匿名方法是通过使用 delegate 关键字创建委托实例来声明的
delegate void NumberChanger(int n);
...
NumberChanger nc = delegate(int x)
{
Console.WriteLine("Anonymous Method: {0}", x);
};
https://www.runoob.com/csharp/csharp-anonymous-methods.html
17.多线程和线程锁:
https://blog.csdn.net/afei__/article/details/80468265
线程是轻量级进程
线程生命周期开始于 System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时
未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。
就绪状态:当线程准备好运行并等待 CPU 周期时的状况。
不可运行状态:下面的几种情况下线程是不可运行的:
已经调用 Sleep 方法
已经调用 Wait 方法
通过 I/O 操作阻塞
死亡状态:当线程已完成执行或已中止时的状况。
进程中第一个被执行的线程称为主线程。
锁和死锁
这个是在多线程中用到,为了线程安全来使用线程锁控制资源的利用和释放,比如最直观的售票系统,同一张票只能在一个窗口卖,且剩余票不能是负数,否则就是线程不安全得
为了防止这种情况发生 加入了线程锁,但是使用不当,比如一个进程占用了一个资源,但是又去请求新的资源,但是新的资源被其他进程占用,他又没有释放当前资源,这样下一个进程请求该资源就会造成堵塞,也就是死锁,为了防止这种情况发生
加锁顺序(线程按照一定的顺序加锁)
加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
死锁检测
18.IEnumberable和IQueryable和Tolist()
IEnumberable接口成为迭代器,就一个GetEnumberator()方法;它返回的是一IEnumberator对象这是一个可以循环访问集合的对象,IEnumberator是一个集合访问器。支持foreach语句,IEnumberator定义了Current属性,MoveNext和Reset两个方法;Current用来获取集合中的项,MoveNext方法只是将游标内部位置向前移动(就是移到下一个元素)
IQueryable继承IEnumberable接口,两者都是延时执行
IEnumerable<T> 泛型类在调用自己的SKip 和 Take 等扩展方法之前数据就已经加载在本地内存里了,实际使用时会在内存中执行后续方法获取结果,比较消耗内存,且会一直和数据库保持连接
而IQueryable<T> 是将Skip ,take 这些方法表达式翻译成T-SQL语句之后再向SQL服务器发送命令,它并不是把所有数据都加载到内存里来才进行条件过滤。但仍和数据库保持连接
Tolist()会立即执行,把结果加载到内存中,且和数据库断开连接。
19.viewbag和viewdata
ViewData和ViewBag一般用于View和Controller间传值,
ViewData是key值读取对应的value,ViewData["Name"] = "蝈蝈";
ViewBag是动态类型,使用时直接通过属性赋值即可,ViewBag.Message = "Your application description page.";
ViewData和ViewBag只在当前Action中有效,等同于View
ViewData和ViewBag中的值可以互相访问
20.消息队列
消息队列比作是一个存放消息的容器,当我们需要使用消息的时候可以取出消息供自己使用。
消息队列是分布式系统中重要的组件,使用消息队列主要是为了通过异步处理提高系统性能和削峰、降低系统耦合性。目前使用较多的消息队列有ActiveMQ,RabbitMQ,Kafka,RocketMQ
队列 Queue 是一种先进先出的数据结构,所以消费消息时也是按照顺序来消费的
消息队列最常用的是发布-订阅模式,还有点对点订阅模式(一个消息只有一个消费者)
使用消息队列进行异步处理之后,需要适当修改业务流程进行配合,比如用户在提交订单之后,订单数据写入消息队列,不能立即返回用户订单提交成功,需要在消息队列的订单消费者进程真正处理完该订单之后,甚至出库后,再通过电子邮件或短信通知用户订单成功。最常见的订票系统。
使用消息队列需要注意
①.保证消息的可靠性传输(如何处理消息丢失的问题) https://blog.csdn.net/evilcry2012/article/details/87718450
②.如何保证消息的顺序性? https://blog.csdn.net/evilcry2012/article/details/86715842
20.同步和异步
同步异步 指的是在客户端
同步意味着 客户端提出了一个请求以后,在回应之前只能等待
例子:你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
异步意味着 客户端提出一个请求以后,还可以继续提其他请求
例子:书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。
阻塞非阻塞 指的是服务器端
阻塞意味着 服务器接受一个请求后,在返回结果以前不能接受其他请求
非阻塞意味着 服务器接受一个请求后,尽管没有返回结果,还是可以继续接受其他请求
21.构造函数和析构函数
析构函数:对象所在的函数已调用完毕时,系统自动执行析构函数。特别的一个类可以有多个构造函数,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载。
析构函数:是一种特殊的方法。用于在撤销对象前,完成一些清理工作,比如:释放内存等。 但是一般GC(垃圾回收机制帮我们做掉了)
22.IIS和 IIS Express
IIS是一个系统服务,IIS Express则只是一个临时进程。
IIS需要我们安装,可以手动部署发布的应用程序,也可以直接运行程序会默认设置在IIS的Default Web Site中。有可视化界面
IIS Express集成在了VS中,用于调试,没有可视化界面,有配置文件。
23.枚举
枚举是一组命名整型常量。枚举类型是使用 enum 关键字声明的。
C# 枚举是值类型。换句话说,枚举包含自己的值,且不能继承或传递继承。
enum Day { Sun, Mon, Tue, Wed, Thu, Fri, Sat };
static void Main()
{
int x = (int)Day.Sun;
int y = (int)Day.Fri;
Console.WriteLine("Sun = {0}", x);
Console.WriteLine("Fri = {0}", y);
}
会输出
Sun = 0
Fri = 5
24.DataTable和DataSet
DataSet当成内存中的数据库,DataSet是不依赖于数据库的独立数据集合。所谓独立,就是说,即使断开数据链路,或者关闭数据库,DataSet依然是可用的,DataSet在内部是用XML来描述数据的,DataSet中可能有多个DataTable
DataTable是临时保存数据的网格虚拟表(表示内存中数据的一个表)
25.冒泡排序
int
temp = 0;
int
[] arr = {23, 44, 66, 76, 98, 11, 3, 9, 7};
for
(
int
i = 0; i < arr.Length - 1; i++)
{
for
(
int
j = 0; j < arr.Length - 1 - i; j++)
{
if
(arr[j] > arr[j + 1])
{
temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
26. Skip(跳过) Take(获取) (C#里面集合的扩展方法,常用作分页)
var
list =
new
List<
int
>();
//比如 list里面是 1,2,3,4,5,6,7,8,9,10
var
result = list.Skip(2);
//返回值就是 3,4,5,6,7,8,9,10;
var
result = list.Take(2);
//返回值就是 1,2
//搭配使用,一般用来分页
var
result = list.Skip(2).Take(2);
//返回值 3,4
27.对比数组(Array)/集合(Set)/列表(List)
1、存储内容比较: Array 数组可以包含基本类型和对象类型, ArrayList 却只能包含对象类型。 Array 数组在存放的时候一定是同种类型的元素。ArrayList 就不一定了 。集合Set的对象都是不重复的,而且是无序的。
2、空间大小比较: Array 数组的空间大小是固定的,所以需要事前确定合适的空间大小。 ArrayList 的空间是动态增长的,而且,每次添加新的元素的时候都会检查内部数组的空间是否足够。
3.方法上的比较: ArrayList 方法上比 Array 更多样化,比如添加全部 addAll()、删除全部 removeAll()、返回迭代器 iterator() 等。