美文网首页
.NET 面试题

.NET 面试题

作者: oooooliver | 来源:发表于2018-03-22 17:14 被阅读0次

    面试


    Mock

    Moq

    Moq是针对Net开发的模拟库。他的目标是让模拟以一种自然的方式与现有的单元测试进行集成,使他更加直观。

    我们可以使用nuget,来获取Moq包。或者访问其官网,不过被墙了。

    局限性

    模拟的类不能为密封类。不能直接模拟静态方法。Moq只能创建模拟对象的实例。可以通过模拟的方式让他去访问service,并且根据不同的请求模拟返回响应的结果。

    原理

    Moq只需要一个接口,就可以产生一个对象。利用反射机制来动态生成一个空框架,用于模仿对象的行为。

    Mock
    通过Mock<T>这个类,我们就可以创建一个对象的模拟。T可以使接口或者类。但必须有public和virtual属性。

    通过IFake接口,去动态创建Mock。然后使用Setup方法去创建我们需要的对象。当模拟的对象调用它自己方法的时候,并且参数是Ping。

    最后Return返回对应结果,前面的请求返回结果为真。这是我们指定的返回值。当请求调用DoSomething并且传递Ping,那么就返回true。

            public interface IFake
            {
                bool DoSomething(string actionname);
            }
    
            [TestMethod]
            public void Test_Interface_IFake()
            {
                var mo = new Mock<IFake>();
                mo.Setup(f => f.DoSomething("Ping")).Returns(true);
                mo.Setup(f => f.DoSomething("PingF")).Returns(false);
                Assert.AreEqual(true, mo.Object.DoSomething("Ping"));
                Assert.AreEqual(false, mo.Object.DoSomething("PingF"));
            }
    

    DB 分页

    表中主键必须为标识列,[ID] int IDENTITY (1,1)

    分页方案一 (利用Not In和SELECT TOP分页)

    语句形式:

    SELECT TOP 10 *
    FROM TestTable
    WHERE (ID NOT IN
              (SELECT TOP 20 id
             FROM TestTable
             ORDER BY id))
    ORDER BY ID
    
    
    SELECT TOP 页大小 *
    FROM TestTable
    WHERE (ID NOT IN
              (SELECT TOP (页大小*(页数-1)) id
             FROM 表
             ORDER BY id))
    ORDER BY ID
    

    分页方案二 (利用ID大于多少和SELECT TOP分页)

    语句形式:

    SELECT TOP 10 *
    FROM TestTable
    WHERE (ID >=
              (SELECT MAX(id)
             FROM (SELECT TOP 21 id
                     FROM TestTable
                     ORDER BY id) AS T))
    ORDER BY ID
    
    
    SELECT TOP 页大小 *
    FROM TestTable
    WHERE (ID >=
              (SELECT MAX(id)
             FROM (SELECT TOP (页大小*(页数-1)+1) id
                     FROM 表
                     ORDER BY id) AS T))
    ORDER BY ID
    

    分页方案三:(利用SQL的游标存储过程分页)

    create  procedure SqlPager
    @sqlstr nvarchar(4000), --查询字符串
    @currentpage int, --第N页
    @pagesize int --每页行数
    as
    set nocount on
    declare @P1 int, --P1是游标的id
     @rowcount int
    exec sp_cursoropen @P1 output,@sqlstr,@scrollopt=1,@ccopt=1, @rowcount=@rowcount output
    select ceiling(1.0*@rowcount/@pagesize) as 总页数--,@rowcount as 总行数,@currentpage as 当前页 
    set @currentpage=(@currentpage-1)*@pagesize+1
    exec sp_cursorfetch @P1,16,@currentpage,@pagesize 
    exec sp_cursorclose @P1
    set nocount off
    

    其它的方案:如果没有主键,可以用临时表,也可以用方案三做,但是效率会低。
    建议优化的时候,加上主键和索引,查询效率会提高。

    通过SQL 查询分析器,显示比较:我的结论是:

    分页方案二:(利用ID大于多少和SELECT TOP分页)效率最高,需要拼接SQL语句
    分页方案一:(利用Not In和SELECT TOP分页) 效率次之,需要拼接SQL语句
    分页方案三:(利用SQL的游标存储过程分页) 效率最差,但是最为通用

    目前我主要是用第一,第二种方案


    With(Nolock)

    什么是

    开发人员喜欢在SQL脚本中使用WITH(NOLOCK), WITH(NOLOCK)其实是表提示(table_hint)中的一种。它等同于 READUNCOMMITTED 。 具体的功能作用如下所示 摘自MSDN

    1. 指定允许脏读。不发布共享锁来阻止其他事务修改当前事务读取的数据,其他事务设置的排他锁不会阻碍当前事务读取锁定数据。允许脏读可能产生较多的并发操作,但其代价是读取以后会被其他事务回滚的数据修改。这可能会使您的事务出错,向用户显示从未提交过的数据,或者导致用户两次看到记录(或根本看不到记录)。有关脏读、不可重复读和幻读的详细信息,请参阅并发影响。

    2. READUNCOMMITTED 和 NOLOCK 提示仅适用于数据锁。所有查询(包括那些带有 READUNCOMMITTED 和 NOLOCK 提示的查询)都会在编译和执行过程中获取 Sch-S(架构稳定性)锁。因此,当并发事务持有表的 Sch-M(架构修改)锁时,将阻塞查询。例如,数据定义语言 (DDL) 操作在修改表的架构信息之前获取 Sch-M 锁。所有并发查询(包括那些使用 READUNCOMMITTED 或 NOLOCK 提示运行的查询)都会在尝试获取 Sch-S 锁时被阻塞。相反,持有 Sch-S 锁的查询将阻塞尝试获取 Sch-M 锁的并发事务。有关锁行为的详细信息,请参阅锁兼容性(数据库引擎)。

    3. 不能为通过插入、更新或删除操作修改过的表指定 READUNCOMMITTED 和 NOLOCK。SQL Server 查询优化器忽略 FROM 子句中应用于 UPDATE 或 DELETE 语句的目标表的 READUNCOMMITTED 和 NOLOCK 提示

    使用场景

    什么时候可以使用WITH(NOLOCK)? 什么时候不能使用WITH(NOLOCK),这个要视你系统业务情况,综合考虑性能情况与业务要求来决定是否使用WITH(NOLOCK), 例如涉及到金融或会计成本之类的系统,出现脏读那是要产生严重问题的。关键业务系统也要慎重考虑。大体来说一般有下面一些场景可以使用WITH(NOLOCK)

    1. 基础数据表,这些表的数据很少变更。

    2. 历史数据表,这些表的数据很少变更。

    3. 业务允许脏读情况出现涉及的表。

    4. 数据量超大的表,出于性能考虑,而允许脏读。

    另外一点就是不要滥用WITH(NOLOCK),我发现有个奇怪现象,很多开发知道WITH(NOLOCK),但是有不了解脏读,习惯性的使用WITH(NOLOCK)。

    区别

    NOLOCK这样的写法,其实NOLOCK其实只是别名的作用,而没有任何实质作用。所以不要粗心将(NOLOCK)写成NOLOCK

    使用WITH(NOLOCK)后,数据库并不是不生成相关锁。 对比可以发现使用WITH(NOLOCK)后,数据库只会生成DB类型的共享锁、以及TAB类型的架构稳定性锁

    update table 会阻止select 有(nolock)不会有这个阻止
    alter table 始终会阻止select不论有没有(nolock)


    抽象类和接口

    抽象类

    抽象类是特殊的类,只是不能被实例化;除此以外,具有类的其他特性;重要的是抽象类可以包括抽象方法,这是普通类所不能的。抽象方法只能声明于抽象类中,且不包含任何实现,派生类必须覆盖它们。另外,抽象类可以派生自一个抽象类,可以覆盖基类的抽象方法也可以不覆盖,如果不覆盖,则其派生类必须覆盖它们。

    接口

    接口是引用类型的,类似于类,和抽象类的相似之处有三点:

    1. 不能实例化;

    2. 包含未实现的方法声明;

    3. 派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员);

    另外,接口有如下特性:
    接口除了可以包含方法之外,还可以包含属性、索引器、事件,而且这些成员都被定义为公有的。除此之外,不能包含任何其他的成员,例如:常量、域、构造函数、析构函数、静态成员。一个类可以直接继承多个接口,但只能直接继承一个类(包括抽象类)。

    抽象类和接口的区别

    1. 类是对对象的抽象,可以把抽象类理解为把类当作对象,抽象成的类叫做抽象类.而接口只是一个行为的规范或规定,微软的自定义接口总是后带able字段,证明其是表述一类类“我能做。。。”.抽象类更多的是定义在一系列紧密相关的类间,而接口大多数是关系疏松但都实现某一功能的类中.

    2. 接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法;

    3. 一个类一次可以实现若干个接口,但是只能扩展一个父类

    4. 接口可以用于支持回调,而继承并不具备这个特点.

    5. 抽象类不能被密封。

    6. 抽象类实现的具体方法默认为虚的,但实现接口的类中的接口方法却默认为非虚的,当然您也可以声明为虚的.

    7. (接口)与非抽象类类似,抽象类也必须为在该类的基类列表中列出的接口的所有成员提供它自己的实现。但是,允许抽象类将接口方法映射到抽象方法上。

    8. 抽象类实现了oop中的一个原则,把可变的与不可变的分离。抽象类和接口就是定义为不可变的,而把可变的座位子类去实现。

    9. 好的接口定义应该是具有专一功能性的,而不是多功能的,否则造成接口污染。如果一个类只是实现了这个接口的中一个功能,而不得不去实现接口中的其他方法,就叫接口污染。

    10. 尽量避免使用继承来实现组建功能,而是使用黑箱复用,即对象组合。因为继承的层次增多,造成最直接的后果就是当你调用这个类群中某一类,就必须把他们全部加载到栈中!后果可想而知.(结合堆栈原理理解)。同时,有心的朋友可以留意到微软在构建一个类时,很多时候用到了对象组合的方法。比如asp.net中,Page类,有Server Request等属性,但其实他们都是某个类的对象。使用Page类的这个对象来调用另外的类的方法和属性,这个是非常基本的一个设计原则。

    11. 如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法.

    IOC 控制反转

    控制反转(Inversion of Control,英文缩写为IoC)把创建对象的权利交给框架 ,是框架的重要特征,并非面向对象编程的专用术语。它包括依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)

    例子

    WPF 中的MEF框架

    class Program
        {
            [Import("MusicBook")]
            public IBookService Service { get; set; }
    
            static void Main(string[] args)
            {
                Program pro = new Program();
                pro.Compose();
                if (pro.Service != null)
                {
                    Console.WriteLine(pro.Service.GetBookName());
                }
                Console.Read();
            }
            //宿主MEF并组合部件
            private void Compose()
            {
                //获取包含当前执行的代码的程序集
                var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
                CompositionContainer container = new CompositionContainer(catalog);
                //将部件(part)和宿主程序添加到组合容器
                container.ComposeParts(this);
            }
        }
    }
    
    ////Export(typeof(IBookService)) 这句话将类声明导出为IBookService接口类型
        //[Export(typeof(IBookService))]
        [Export("MusicBook",typeof(IBookService))]
        class MusicBook:IBookService
        {
    
            public string BookName { get; set; }
    
            public string GetBookName()
            {
                return "音乐书本";
            }
        }
    

    SOLID

    单一职责原则(SRP)
    开放封闭原则(OCP)
    里氏替换原则(LSP)
    接口隔离原则(ISP)
    依赖倒置原则(DIP)


    单例模式

    /// <summary>
        /// 单例模式3:线程安全(建议用这种方式)
        /// </summary>
        public class Singleton
        {
            private static Singleton _instance = null;
            private static readonly object _instanceLock = new object();
            private Singleton() { }
            public static Singleton Instance
            {
                get
                {
                    if (null == _instance)
                    {
                        lock (_instanceLock)
                        {
                            if (null == _instance)
                            {
                                _instance = new Singleton();
                            }
                        }
                    }
                    return _instance;
                }
            }
        }
    

    优点

    系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。

    缺点

    当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new,可能会给其他开发人员造成困扰,特别是看不到源码的时候。

    适用场合

    需要频繁的进行创建和销毁的对象;
    创建对象时耗时过多或耗费资源过多,但又经常用到的对象;
    工具类对象;
    频繁访问数据库或文件的对象。

    Async/Await

            async Task<int> AccessTheWebAsync()
            {
                HttpClient client = new HttpClient();
                Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
                DoIndependentWork();
                string urlContents = await getStringTask;
                return urlContents.Length;
            }
    
            private void DoIndependentWork()
            {
                //throw new NotImplementedException();
            }
    
    1. 调用异步方法AccesstheWebAsync
    2. 创建HttpClient实例,并使用HttpClient获取异步数据。
    3. 利用Task执行获取数据方法(假设获取数据需要很长时间),不阻塞当前线程,getStringTask代表进行中的任务。
    4. 因为getStringTask还没有使用await 关键字,使之可以继续执行不依赖于其返回结果的其他任务,同步执行DoIndependentWork。
    5. 当同步任务DoIndependentWork执行完毕之后,返回调用给AccessTheWebAsync线程。
    6. 使用await强制等待getStringTask完成,并获取基于Task<String>类型的返回值。(如果getStringTask在同步方法DoIndependentWork执行之前完成,调用会返回给AccessTheWebAsync线程,调用await将会执行不必要的挂起操作)
      当获取web数据之后,返回结果记录在Task中并返回给await调用处(当然,返回值并没有在第二行返回)。
      获取数据并返回计算结果。

    结论:

    1. Await使用异步IO线程来执行,异步操作的任务,释放工作线程回线程池。
    2. 线程池分为工作线程和异步IO线程,分别执行不同级别的任务。
    3. 使用Await来执行异步操作效率并不总是高于同步操作,需要根据异步执行长短来判断。
    4. 当工作线程和IO线程相互切换时,会有一定性能消耗。

    Thread, Task, Async/Await, Parallel, IAsyncResult

    线程同步的方式

    volatile

    volatile是最简单的一种同步方法,当然简单是要付出代价的。它只能在变量一级做同步,volatile的含义就是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我。(【转自www.bitsCN.com 】)因此,当多线程同时访问该变量时,都将直接操作主存,从本质上做到了变量共享

    但volatile并不能实现真正的同步,因为它的操作级别只停留在变量级别,而不是原子级别。如果是在单处理器系统中,是没有任何问题的,变量在主存中没有机会被其他人修改,因为只有一个处理器,这就叫作processor Self-Consistency。但在多处理器系统中,可能就会有问题。 每个处理器都有自己的data cach,而且被更新的数据也不一定会立即写回到主存。所以可能会造成不同步,但这种情况很难发生,因为cach的读写速度相当快,flush的频率也相当高,只有在压力测试的时候才有可能发生,而且几率非常非常小

    lock

    System.Threading.Interlocked

    Monitor

    Mutex

    ReaderWriterLock


    Web API

    什么是

    强调两个关键点,即可以对接各种客户端(浏览器,移动设备),构建http服务的框架

    Web API在ASP.NET完整框架中地位如下图,与SignalR一起同为构建Service的框架。Web API负责构建http常规服务,而SingalR主要负责的是构建实时服务,例如股票,聊天室,在线游戏等实时性要求比较高的服务

    为什么需要 Web API

    Web API最重要的是可以构建面向各种客户端的服务。另外与WCF REST Service不同在于,Web API利用Http协议的各个方面来表达服务(例如 URI/request response header/caching/versioning/content format),因此就省掉很多配置。
    当你遇到以下这些情况的时候,就可以考虑使用Web API了。

    • 需要Web Service但是不需要SOAP
    • 需要在已有的WCF服务基础上建立non-soap-based http服务
    • 只想发布一些简单的Http服务,不想使用相对复杂的WCF配置
    • 发布的服务可能会被带宽受限的设备访问
    • 希望使用开源框架,关键时候可以自己调试或者自定义一下框架

    主要功能

    Web API的主要功能

    1. 支持基于Http verb (GET, POST, PUT, DELETE)的CRUD (create, retrieve, update, delete)操作

      通过不同的http动作表达不同的含义,这样就不需要暴露多个API来支持这些基本操作。

    2. 请求的回复通过Http Status Code表达不同含义,并且客户端可以通过Accept header来与服务器协商格式,例如你希望服务器返回JSON格式还是XML格式。

    3. 请求的回复格式支持 JSON,XML,并且可以扩展添加其他格式。

    4. 原生支持OData。

    5. 支持Self-host或者IIS host。

    6. 支持大多数MVC功能,例如Routing/Controller/Action Result/Filter/Model Builder/IOC Container/Dependency Injection

    Web API vs WCF

    发布服务在Web API和WCF之间该如何取舍呢?这里提供些简单地判断规则,

    • 如果服务需要支持One Way Messaging/Message Queue/Duplex Communication,选择WCF
    • 如果服务需要在TCP/Named Pipes/UDP (wcf 4.5),选择WCF
    • 如果服务需要在http协议上,并且希望利用http协议的各种功能,选择Web API
    • 如果服务需要被各种客户端(特别是移动客户端)调用,选择Web API

    RESTful

    什么是RESTful架构

    1. 每一个URI代表一种资源;
      
      2. 客户端和服务器之间,传递这种资源的某种表现层;

    3. 客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。


    线程和进程的区别

    线程是资源调度的最小单位,进程是cpu调度的最小单位,线程包含多个进程,他们共享进程的资源
    进程创建和切换开销大,而线程小


    聚簇索引和非聚餐索引

    聚集索引

    表数据按照索引的顺序来存储的,也就是说索引项的顺序与表中记录的物理顺序一致。对于聚集索引,叶子结点即存储了真实的数据行,不再有另外单独的数据页。 在一张表上最多只能创建一个聚集索引,因为真实数据的物理顺序只能有一种。

    非聚集索引

    表数据存储顺序与索引顺序无关。对于非聚集索引,叶结点包含索引字段值及指向数据页数据行的逻辑指针,其行数量与数据表行数据量一致。

    相关文章

      网友评论

          本文标题:.NET 面试题

          本文链接:https://www.haomeiwen.com/subject/glzaqftx.html