.NET面试题系列(一)汇总

序言

Socket

为什么需要虚方法?

为了让子类重写

-------------------

什么是CLR

  CLR常用简写词语,CLR是公共语言运行库(Common Language Runtime)和Java虚拟机一样也是一个运行时环境,它负责资源管理(内存分配和垃圾收集等),并保证应用和底层操作系统之间必要的分离。CLR存在两种不同的翻译名称:公共语言运行库和公共语言运行时。

  CLR是一个可由多种编程语言使用的运行环境。CLR的核心功能包括:内存管理、程序集加载、安全性、异常处理和线程同步,可由面向CLR的所有语言使用。并保证应用和底层操作系统之间必要的分离。CLR是.NET Framework的主要执行引擎。

  为了提高平台的可靠性,以及为了达到面向事务的电子商务应用所要求的稳定性级别,CLR还要负责其他一些任务,比如监视程序的运行。按照.net的说法,在CLR监视之下运行的程序属于“托管的”(managed)代码,而不在CLR之下、直接在裸机上运行的应用或者组件属于“非托管的”(unmanaged)的代码。其中C#是托管代码,C++是非托管代码。

什么是CLI?

  通用语言基础结构(Common Language Infrastructure,CLI)是CLR的一个子集,也就是.NET中最终对编译成MSIL代码的应用程序的运行环境进行管理的那一部分。在 CLR结构图中CLI位于下半部分,主要包括类加载器(Class Loader)、实时编译器(IL To Native Compilers)和一个运行时环境的垃圾收集器(GarbageCollector)。CLI是.Net和CLR的灵魂,CLI为IL代码提供运行的环境,你可以将使用任何语言编写的代码通过其特定的编译器转换为 MSIL代码之后运行其上,甚至还可以自己写MSIL代码在CLI上面运行。

 IL是什么

   Intermediate Language (IL)微软中间语言。

  C#源代码通过LC转为IL代码,IL主要包含一些元数据和中间语言指令;

  JIT编译器把IL代码转为机器识别的机器代码。如下图

  语言编译器:无论是VB code还是C# code都会被Language Compiler转换为MSIL

  MSIL的作用:MSIL包含一些元数据和中间语言指令

  JIT编译器的作用:根据系统环境将MSIL中间语言指令转换为机器码

JIT是什么,它是如何工作的?

  JIT(Just In Time简称JIT)是.Net边运行边编译的一种机制。

工作方式:

  开发人员需要通过IL与CLR进行交流, 虽然IL本身支持一些面向对象的概念, 但是对于开发人员来讲还是过于复杂低效, 于是C#应运而生, 程序员只需编写C#代码, csc编译器会将其翻译成IL;虽然CLR理解IL, 但是CPU只认识二进制指令,

  所以CLR需要JIT的帮助, 将IL翻译成CPU指令. JIT按需工作, 当一个.NET方法即将被执行时,JIT会介入, 把该方法(IL指令) 编译成CPU指令, 并保存以供重用.

堆栈数据结构的区别:

  堆(数据结构):堆可以看做是一棵树;例如:堆排序;

  栈(数据结构):一种先进后出的数据结构

堆栈空间分配:

  栈(操作系统):由操作系统自动分配释放,存放函数的变量值,局部变量的值等等,其操作方式类似于数据结构中的栈;

  堆(操作系统):一般由开发者分配释放,若不释放,程序结束时可能会有OS回收,分配方式倒是类似于链表;

  栈是自行维护的,也就是说内存自动维护栈,当栈顶的盒子不再被使用,它将被抛出。相反的,堆需要考虑垃圾回收,垃圾回收用于保持堆的整洁性。

值类型和引用类型

  值类型:bool,byte ,char,decimal,double,enum,float,int,long,sbyte,short,struct,uint,ulong,ushort

  引用类型:各种class类、string、数组、接口、委托、object

装箱和拆箱

  装箱:将值类型→引用类型

  拆箱:将引用类型→值类型

 装箱操作和拆箱操作是要额外耗费cpu和内存资源的,所以在c# 2.0之后引入了泛型来减少装箱操作和拆箱操作消耗。

泛型

  泛型出现的一个很重要的原因,就是需要创建能够容纳任何类型的容器类的需求。之前用的是object来存储。但是这样会导致可以存多种类型,我们确实需要容器能够支持不同类型,但是具体存储的时候,我们希望只能存储一种,以防止错误发生,然而object会接受任何类型,这就导致泛型的出现 泛型的本质就是暂时不指定类型,稍后制定类型,一旦指定,就必须存储这一种类型的对象了 。

/// <summary>
    /// 泛型方法
    /// </summary>
    public class GenericsMethord
    {
        //这里介绍泛型方法,在之前02-泛型的引入中,使用的就是泛型方法,这里再重复一次
        /*
        详解:T为泛型的一个代表,换成别的字母同样可以
              T和A代表的类型在使用时才声明,俗称“延迟声明”
        */
        public static void ShowModel<T, A>(T model1, A model2)
            where T : ModelFather
            where A : model3
        {
            Console.WriteLine("id值为:" + model1.id + "  name值为:" + model1.name);
            Console.WriteLine("id值为:" + model2.id + "  name值为:" + model2.name);
        }
    }
View Code

自动属性

  1. 传统的方式在类中声明一个属性,需要先声明一个私有变量的字段,然后在配合公有属性,如下面的:userId属性。

  2.利用自动属性:不需要字段,声明一个空属性,直接get,set(快捷键:prop),编译时编译器为我们生成存取数据的字段. 如下面的:userName属性。

public class userInfor
    {
        //私有字段
        private string _userId;
        //公有属性
        public string userId
        {
            get
            {
                return _userId;
            }
            set
            {
                _userId = value;
            }
        }

        public string useName { get; set; }


        /// <summary>
        /// 为了后面的反射调用
        /// </summary>
        public void Test()
        {
            Console.WriteLine("我是一个方法");
        }

    }
View Code

可选参数

 public static void Test(string useName,string userPwd,int userAge=16,string userSex="")
        {
            Console.WriteLine("userName:{0},userPwd:{1},userAge:{2},userSex:{3}", useName, userPwd, userAge, userSex);
        }

匿名类

// 匿名类(匿名类通过new关键字实现)
 Console.WriteLine("------------------------------匿名类(匿名类通过new关键字实现)------------------------------");
 var test1 = new
     {
         id = "1",
         name = "maru1"
     };
 Console.WriteLine("id为{0},name为{1}", test1.id, test1.name);

匿名方法

       {
                 NoReturnWithPara methord = (id, name) => Console.WriteLine("{0} {1}", id, name);
                 methord.Invoke(7, "唐马儒7");
                 methord(7, "唐马儒7");
         }

扩展方法

  扩展方法即对一些类型进行方法的扩展,扩展方法的三要素为:静态类、静态方法、this关键字。

  使用方法:this后面的那个扩展类型.方法名。

  下面对string类型扩展,使其可以将string类型转换成int类型,将MyExtend.ToInt(p1) 改装成 p1.ToInt()。

/// <summary>
    /// 扩展方法的三要素:静态类、静态方法、this关键字
    /// 使用方法:this关键后面对应的参数类型.扩展方法名
    /// </summary>
    public static class MyExtend
    {
        /// <summary>
        /// 1.对string类型进行扩展
        /// 将MyExtend.ToInt(p1) 改装成 p1.ToInt()
        /// </summary>
        /// <param name="p1">需要转换的string对象</param>
        /// <returns></returns>
        public static int ToInt(this string p1)
        {
            return Convert.ToInt32(p1);
        }
        
        /// <summary>
        /// 2.对string类型进行扩展
        /// 将p1这个string类型转换成int类型,然后加上p2,最后输出
        /// 将MyExtend.ToInt(p1,p2) 改装成 p1.ToInt(p2)
        /// </summary>
        /// <param name="p1">需要转换的string对象</param>
        /// <param name="p2">被加的数</param>
        /// <returns></returns>
        public static int ToInt(this string p1,int p2)
        {
            return Convert.ToInt32(p1)+p2;
        }
        /// <summary>
        /// Lambda模式的where方法自己实现
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="func"></param>
        /// <returns></returns>
        public static IEnumerable<TSource> YpfWhere<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> func)
        {
            List<TSource> studentList = new List<TSource>();
            foreach (TSource item in source)
            {
                bool bResult = func.Invoke(item);
                if (bResult)
                {
                    studentList.Add(item);
                }
            }
            return studentList;
        }
    }
View Code

  方法调用

Console.WriteLine("------------------------------ 三. 扩展方法----------------------------------");
            {
                {
                    //3.1 对string类型两个扩展方法的调用
                    Console.WriteLine("------------------------------ 3.1 对string类型两个扩展方法的调用----------------------------------");
                    string msg1 = "123";
                    //通过静态方法调用
                    Console.WriteLine(MyExtend.ToInt(msg1));
                    //通过扩展方法调用
                    Console.WriteLine(msg1.ToInt());
                    Console.WriteLine(msg1.ToInt(1));
                }

            }
View Code

  结果

进程与线程的区别

  进程(process)和线程(thread)是操作系统的基本概念

  进程就好比工厂的车间,它代表CPU所能处理的单个任务。线程就好比车间里的工人。一个进程可以包括多个线程。车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。

聚集索引和非聚集索引

   https://blog.csdn.net/zc474235918/article/details/50580639

深拷贝浅拷贝

   深复制和浅复制最根本的区别在于是否是真正获取了一个对象的复制实体,而不是引用。

  浅复制 —-只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做“(浅复制)浅拷贝”,换句话说,浅复制仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变。

  深复制 —-在计算机中开辟了一块新的内存地址用于存放复制的对象。

说说常用的锁,lock是一种什么样的锁?

  常用的如如SemaphoreSlim、ManualResetEventSlim、Monitor、ReadWriteLockSlim,lock是一个混合锁,其实质是Monitor['mɒnɪtə]。

lock为什么要锁定一个参数,可不可锁定一个值类型?这个参数有什么要求?

  lock的锁对象要求为一个引用类型。她可以锁定值类型,但值类型会被装箱,每次装箱后的对象都不一样,会导致锁定无效。

  对于lock锁,锁定的这个对象参数才是关键,这个参数的同步索引块指针会指向一个真正的锁(同步块),这个锁(同步块)会被复用。

Mutex和lock有何不同?一般用哪一个作为锁使用更好?

  Mutex是一个基于内核模式的互斥锁,支持锁的递归调用,而Lock是一个混合锁,一般建议使用Lock更好,因为lock的性能更好。

行内元素和块级元素的具体区别是什么

C#中的弱引用——WeakReference

一:什么是弱引用

了解弱引用之前,先了解一下什么是强引用

  例如 : Object obj=new Object(); 就是一个强引用,内存分配一份空间给用以存储Object数据,这块内存有一个首地址,也就是obj所保存的数据,内存分配的空间中不仅仅保存着Object对象信息,还保存着自己(Object本身)被引用的次数。

  当一个对象被强引用的形式创建的时候,本身被引用的次数已经为1.

  接着Object o=obj; 这句代码执行之后,obj指向的Object的存储空间已经被引用了2次,所以Object保存的被引用数值为2.

  总结:强引用最终导致的结果就是被引用的对象的被引用次数+1;

  相反的弱引用就是不会对被引用对象的被引用次数有任何影响。

二:弱引用有什么作用

  防止内存泄露。

  Object obj=new Object();

  当你在通过异步的形式访问网络上面的资源的时候,需要的时间可能会比较长,在数据返回之前,用户很可能转向了其他的页面,如果异步访问的对象(obj)对本地的一个对象(Object)是强引用的话,那么在这个异步访问对象(obj)被释放之前,也即在数据被回调之前,这个被引用的对象(Object)是不会被销毁的,这样一来,就导致内存一直被占用。

  WeakReference weakObj=new WeakReference(Object);

  此时就可以使用弱引用,弱引用对象(weakObj)发出异步请求,在回调之前,如果用户要转到其他的页面,这个被引用的对象(Object)是可以被释放的,这样子就不会出现内存一直被占用的现象。

三:怎样使用弱引用

  弱引用类: WeakReference //有两个重载的构造函数

  WeakReference WeakObj=new WeakReference(Object);//弱引用方式

  IsAlive属性是判断此弱引用对象所引用的对象是否还存在,存在:IsAlive=True;

Target属性是设置该弱引用对象所引用的数据对象的值

 C#Equals和= =(等于号)的比较

1.值类型的比较

对于值类型来说  两者比较的都是”内容”是否相同,即值是否一样,很显然此时两者是划等号的。

2. 引用类型的比较

 对于引用类型来说,等号(==)比较的是两个变量的”引用” 是否一样,即是引用的”地址”是否相同。而对于equals来说仍然比较的是变量的  ”内容” 是否一样

 由于string是微软封装的一个字符串类,在内部他已经对 = = 操作符进行了重写。重写后他比较的则是两个变量的内容是否相同

资料

https://www.cnblogs.com/cyq1162/p/9073634.html

https://www.cnblogs.com/1996V/p/9037603.html

原文地址:https://www.cnblogs.com/cnki/p/8191859.html