【2017年新篇章】 .NET 面试题汇总(二)

本次给大家介绍的是我收集以及自己个人保存一些.NET面试题第二篇

第一篇文章请到这里:【2017年新篇章】 .NET 面试题汇总(一)

简介

  • 此次包含的不止是.NET知识,也包含少许前端知识以及.net面试时所涉及的种种考点,希望能给找工作的同学们哪怕一点点帮助。

在此提醒下,本文适合:

  • 刚毕业的萌新
  • 工作不久换工作的
  • 大牛可忽略啦

1.接口

文章引用:http://www.cnblogs.com/jiajiayuan/archive/2011/09/16/2178462.html

①.接口的特性:

  1. 接口类似于抽象基类,不能直接实例化接口;接口中的方法都是抽象方法,实现接口的任何非抽象类型都必须实现接口的所有成员:
  • 当显式实现该接口的成员时,实现的成员不能通过类实例访问,只能通过接口实例访问。
  • 当隐式实现该接口的成员时,实现的成员可以通过类实例访问,也可以通过接口实例访问,但是实现的成员必须是公有的。
  1. 接口不能包含常量、字段、运算符、实例构造函数、析构函数或类型、不能包含静态成员。
  2. 接口成员是自动公开的,且不能包含任何访问修饰符。
  3. 接口自身可从多个接口继承,类和结构可继承多个接口,但接口不能继承类。

②为什么不能指定接口中方法的修饰符?

接口中的方法用来定义对象之间通信的契约,指定接口中的方法为私有或保护没有意义。它们默认为公有方法。

interface IProgram
    {
        void Fun();
    }
    class Program:IProgram
    {
         //显式实现接口成员
        void IProgram.Fun()
        {
            Console.WriteLine("I am Fun.");
        }
        staticvoid Main(string[] args)
        {
            IProgram p =new Program();  //声明一个接口实例,但不是对接口进行实例化
            p.Fun();
            Console.Read();
        }
    }

③实现接口可以显式实现和隐式实现,那么这两种实现到底有什么优缺点呢?

一般情况,当类或者结构要实现的是单个接口,可以使用隐式实现。
如果类或者结构继承了多个接口且接口中具有相同名称成员时,就要用到显式实现,当显式实现方式存在时,隐式实现方式就失效了。

interface IProgram
    {
        void Fun();
    }
    interface IAProgram
    {
        void Fun();
    }
    class Program : IProgram, IAProgram
    {
        void IProgram.Fun()  //显式实现接口IProgram
        {
            Console.WriteLine("I am IProgram Fun.");
        }
        void IAProgram.Fun()  //显式实现接口IAProgram
        {
            Console.WriteLine("I am IAProgram Fun.");
        }
        //public void Fun()   //隐式实现接口
        //{
        //    Console.WriteLine("I am Program Fun.");
        //}
        staticvoid Main(string[] args)
        {
            //IProgram p = new Program();
            //p.Fun();
            //IAProgram ap = new Program();
            //ap.Fun();
            Program pro =new Program();
            ((IProgram)pro).Fun();
            ((IAProgram)pro).Fun();
            Console.Read();
        }
    }

④接口的继承:

接口继承和类继承不同

  1. 首先,类继承不仅是说明继承,而且也是实现继承;而接口继承只是说明继承。
    也就是说,派生类可以继承基类的方法实现,而派生的接口只继承了父接口的成员方法说明,而没有继承父接口的实现;
  2. 其次,C#中类继承只允许单继承,但是接口继承允许多继承,一个子接口可以有多个父接口。
    接口可以从零或多个接口中继承。从多个接口中继承时,用":"后跟被继承的接口名字,多个接口名之间用","分割。
    被继承的接口应该是可以访问得到的,比如从private 类型或internal 类型的接口中继承就是不允许的。
    接口不允许直接或间接地从自身继承。和类的继承相似,接口的继承也形成接口之间的层次结构。
interface IProgram
    {
        void Fun();
    }
    interface IAProgram:IProgram
    {
        
    }
    class Program :  IAProgram
    {
        void IProgram.Fun()
        {
            Console.WriteLine("I am IProgram Fun.");
        }
        static void Main(string[] args)
        {
            Program pro =new Program();
            ((IAProgram)pro).Fun();
            Console.Read();
        }
    }

⑤接口的覆盖:

由于接口的实现没有方法体,抽象方法也没有方法体,那么当我们在接口的实现方法里调用抽象方法时,会如何执行呢?

interface IProgram
    {
        void Fun();
    }
    abstract class AProgram : IProgram
    {
        public abstract void AFun();
        void IProgram.Fun()
        {
            AFun();
        }
    }
    class Program:AProgram
    {
        public override void AFun()
        {
            Console.WriteLine("I am AProgram.");
        }
        static void Main(string[] args)
        {
            IProgram pro =new Program();
            pro.Fun();
            Console.Read();
        }
    }
//结果:I am Aprogram.

通过断点,可以看到,当执行pro.Fun();时,首先会跳到接口的实现方法里,然后去调用抽象函数的实现方法,当抽象函数的方法实现后,再回到接口的实现方法,直到执行完成

当我们在实现接口的方法里调用虚函数呢?

interface IProgram
    {
        void Fun();
    }
    class AProgram : IProgram
    {
        public virtual void AFun()    //注意这里是虚函数
        {
            Console.WriteLine("I am virtual AFun.");
        }
        void IProgram.Fun()
        {
            AFun();
        }
    }
    class Program:AProgram
    {
        public override void AFun()  //这里是Override重写
        {
            Console.WriteLine("I am override AFun.");
        }
        static void Main(string[] args)
        {
            IProgram pro =new Program();
            pro.Fun();
            Console.Read();
        }
    }

这时,我们发现,执行的顺序和上一个例子是相同的。所以结果为:I am override AFun.
由此,我们可以继续联想,当我们把override关键字,换成new呢?是不是也是同样的结果,还是和我们以前讲的例子一样,是隐藏呢?

我们把上面的例子进行改进:

interface IProgram
    {
        void Fun();
    }
    class AProgram : IProgram
    {
        public virtual void AFun()
        {
            Console.WriteLine("I am virtual AFun.");
        }
        void IProgram.Fun()
        {
            AFun();
        }
    }
    class Program:AProgram
    {
        public new void AFun()
        {
            Console.WriteLine("I am new AFun.");
        }
        static void Main(string[] args)
        {
            Program pro =new Program();
            ((IProgram)pro).Fun();
            pro.AFun();
            Console.Read();
        }
    }
    

结果为:

I am virtual AFun.
I am new AFun.

由于前面已经讲过了==,这里不在对此进行分析,由此我们可知使用New关键字是对其进行隐藏,当对接口实现的方法里调用的是虚方法时,和类的执行过程是一样的。

⑥接口和抽象类的区别

  1. 接口用于规范,抽象类用于共性。
  2. 接口中只能声明方法,属性,事件,索引器。而抽象类中可以有方法的实现,也可以定义非静态的类变量。
  3. 抽象类是类,所以只能被单继承,但是接口却可以一次实现多个。
  4. 抽象类可以提供某些方法的部分实现,接口不可以。
  5. 抽象类的实例是它的子类给出的。接口的实例是实现接口的类给出的。
  6. 在抽象类中加入一个方法,那么它的子类就同时有了这个方法。而在接口中加入新的方法,那么实现它的类就要重新编写(这就是为什么说接口是一个类的规范了)。
  7. 接口成员被定义为公共的,但抽象类的成员也可以是私有的、受保护的、内部的或受保护的内部成员(其中受保护的内部成员只能在应用程序的代码或派生类中访问)。
  8. 此外接口不能包含字段、构造函数、析构函数、静态成员或常量。

⑦C#中的接口和类有什么异同

  1. 不能直接实例化接口。
  2. 接口不包含方法的实现。
  3. 接口可以实现多继承,而类只能是单继承。
  4. 类定义可在不同的源文件之间进行拆分。

  1. 接口、类和结构可从多个接口继承。
  2. 接口类似于抽象基类:继承接口的任何非抽象类型都必须实现接口的所有成员。
  3. 接口可以包含事件、索引器、方法和属性。
  4. 一个类可以实现多个接口。

2.您在什么情况下会用到虚方法或抽象类,接口?

  1. 如果某个方法可能性在派生类中会被重写。这时就将该方法写为虚方法。
  2. 抽象类:是一个类型,与派生类之间的关系是一个“ISA”的关系。用来做基类,抽象类不能创建对象,类中包括抽象方法和实例方法。
  3. 接口:是设计一个规范,描述了Can do ;与实现类之间是中”LINE A 的关系,C#中接口不能包含字段访问修饰符。

3.重载(Overload )和覆写(Override)的区别

简述:简单的说,一个是同一个函数的几种形式,一个是重写父类函数。

重载:当类包含两个名称相同但签名不同(方法名相同,参数列表不相同)的方法时发生方法重载。用方法重载来提供在语义上完成相同而功能不同的方法。

覆写:在类的继承中使用,通过覆写子类方法可以改变父类虚方法的实现。

区别:

  1. 方法的覆盖是子类和父类之间的关系,是垂直关系;方法的重载是同一个类中方法之间的关系,是水平关系。
  2. 覆盖只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之间的关系。
  3. 覆盖要求参数列表相同;重载要求参数列表不同。
  4. 覆盖关系中,调用那个方法体,是根据对象的类型(对象对应存储空间类型)来决定;重载关系,是根据调用时的实参表与形参表来选择方法体的。

4.值类型和引用类型的区别?写出C#的样例代码。

简述:值类型包括简单类型、结构体类型和枚举类型,引用类型包括自定义类、数组、接口、委托等

  1. 赋值方式:将一个值类型变量赋给另一个值类型变量时,将复制包含的值。这与引用类型变量的赋值不同,引用类型变量的赋值只复制对象的引用,而不复制对象本身。
  2. 派生:值类型不可能派生出新的类型,所有的值类型均隐式派生自 System.ValueType。但与引用类型相同的是,结构也可以实现接口。
  3. null:与引用类型不同,值类型不可能包含 null 值。然而,可空类型功能允许将 null 赋给值类型。
  4. 每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值。
    值类型主要由两类组成:结构、枚举

    ①. 结构分为以下几类:Numeric(数值)类型、整型、浮点型、decimal、bool、用户定义的结构。

    ②. 引用类型的变量又称为对象,可存储对实际数据的引用。声明引用类型的关键字:class、interface、delegate、内置引用类型: object、string

  5. 值类型存贮在中,而引用类型存贮在动态的堆中,栈是先进先出的有系统管理的空间,而堆是由应用程序控制的可随时申请和释放该空间,在Donnet中一般情况下有垃圾收集器处理,他们的不同导致在编程上的不同。
    例:

using System;
using System.Text;
class EventDel
{
    static void Main(string[] args)
    {
        StringBuilder a=new StringBuilder();//将StringBuilder的一个首地址传给a
        StringBuilder b=a;                  //将StringBuilder的一个首地址传给b
        b.Append("mxh");
        Console.WriteLine(a);
        a=null;
        Console.WriteLine(b);
    }
}

"a=null"的意思是:a的引用置为空但此时StringBuilder的堆空间并没有被释放,因此在此之后,输出b时,仍然可以输出mxh

输出结果:
mxh
mxh

5.委托和事件简述

参考资料 :
C# 知识回顾 - 委托 delegate

C# 知识回顾 - 委托 delegate (续)

事件是不是一种委托?

委托是一种安全的函数指针,事件是一种消息机制

委托与事件是什么关系?为什么要使用委托

委托提供了封装方法的方式,事件是某动作已发生的说明,事件是建立于委托之上的。

程序运行时同一个委托能够用来调用不同的方法,只要改变它的引用方法即可,因此委托调节器用的方法不是在编译时决定的,而是在运行时确定的.

6.Session,ViewState,Application,cookie的区别?

  1. Session:用于保持状态的基于 Web 服务器的方法。Session 允许通过将对象存储在Web 服务器的内存中在整个用户会话过程中保持任何对象。主要用于保持代码隐藏类中对象的状态。为每个用户创建的,用于存储单个用户,因为他是相对每个用户的.所以可能来取得在线人数等。
  2. ViewState:主要用于保持 Web 页上控件的状态。当 Web 页上的控件被绑定到代码隐藏类中的对象。
  3. Application 用于存储所有用户都可视的信息.所以它存储的是要让所有用户共享的一些信息.如总访问数等Cache,页面缓存。
  4. Cookie:通常我们都把它放在客户端,也可以存储在服务器端。主要用它存储用户的个性设制,和登陆信息。

7.Application,Session,Cookie,ViewState和Cache生命周期

在ASP.NET中,有很多种保存信息的内置对象,如:Application,Session,Cookie,ViewState和Cache等。下面分别介绍它们的用法和区别。

①.Application对象

Application用于保存所有用户的公共的数据信息,如果使用Application对象,一个需要考虑的问题是任何写操作都要在Application_OnStart事件(global.asax)中完成.尽管使用Application.Lock和Applicaiton.Unlock方法来避免写操作的同步,但是它串行化了对Application对象的请求,当网站访问量大的时候会产生严重的性能瓶颈.因此最好不要用此对象保存大的数据集合. 下面我们做个在线用户统计的例子来说明这个问题:

Global.asax类
   代码
using System;
using System.Collections;
using System.ComponentModel;
using System.Web;
using System.Web.SessionState;
using System.IO;
    /// Global 的摘要说明。
     public class Global : System.Web.HttpApplication
    {
        /// 必需的设计器变量。
        private System.ComponentModel.IContainer components = null;

        private FileStream fileStream;
        private StreamReader reader;//读字符流
        private StreamWriter writer;//写字符流
        
        public Global()
        {
            InitializeComponent();
        }    

        protected void Application_Start(Object sender, EventArgs e)
        {
            Application["CurrentGuests"]=0;//初始花为0;
            fileStream = File.Open(Server.MapPath("counts.text"),FileMode.OpenOrCreate);//文件不存在,创建文件
            reader = new StreamReader(fileStream);//要读取的完整路径
            Application["AllGuests"] = Convert.ToInt32(reader.ReadLine()); //从当前流中读取一行字符并将数据作为字符串返回
            reader.Close();//关闭流
        }
 
        protected void Session_Start(Object sender, EventArgs e)//当用户访问网站时,在线用户+1,总访问数+1
        {
            Application.Lock();//同步,避免同时写入
            
            Application["CurrentGuests"] =(int)Application["CurrentGuests"]+ 1;//总在线用户数
            Application["AllGuests"] =(int)Application["AllGuests"]+ 1;//访问网站的总用户数
            fileStream = new FileStream(Server.MapPath("counts.text"),FileMode.OpenOrCreate,FileAccess.ReadWrite);//
            writer = new StreamWriter(fileStream);//实现一个写入流,使其以一种特定的编码向流中写入字符
            writer.WriteLine(Application["AllGuests"].ToString());//把访问网站的总用户数再次写入到文件
            writer.Close();//关闭写入流

            Application.UnLock();//同步结束
        }
        protected void Session_End(Object sender, EventArgs e)//当前用户退出网站时,在线用户数量-1,
        {
            Application.Lock();
            Application["CurrentGuests"] =(int)Application["CurrentGuests"] - 1;//总在线用户数量-1
            Application.UnLock();    
        }
    (2) WebForm1.aspx
    private void Page_Load(object sender, System.EventArgs e)
        {
            this.Label1.Text = "正在访问站点的用户数:" + Application["CurrentGuests"].ToString();    
            this.Label2.Text ="访问过站点的总用户数:" + Application["AllGuests"].ToString();
        }

②.Session对象

Session用于保存每个用户的专用信息.每个客户端用户访问时,服务器都为每个用户分配一个唯一的会话ID(Session ID) . 她的生存期是用户持续请求时间再加上一段时间(一般是20分钟左右).Session中的信息保存在Web服务器内容中,保存的数据量可大可小.当Session超时或被关闭时将自动释放保存的数据信息.由于用户停止使用应用程序后它仍然在内存中保持一段时间,因此使用Session对象使保存用户数据的方法效率很低.对于小量的数据,使用Session对象保存还是一个不错的选择.使用Session对象保存信息的代码如下:

//存放信息
Session["key"]="value"
//读取数据
string UserName=Session["key"].ToString();

③.Cookie对象

Cookie用于保存客户浏览器请求服务器页面的请求信息,程序员也可以用它存放非敏感性的用户信息,信息保存的时间可以根据需要设置.如果没有设置Cookie失效日期,它们仅保存到关闭浏览器程序为止.如果将Cookie对象的Expires属性设置为Minvalue,则表示Cookie永远不会过期.Cookie存储的数据量很受限制,大多数浏览器支持最大容量为4K,因此不要用来保存数据集及其他大量数据.由于并非所有的浏览器都支持Cookie,并且数据信息是以明文文本的形式保存在客户端的计算机中,因此最好不要保存敏感的,未加密的数据,否则会影响网站的安全性.使用Cookie对象保存的代码如下:

//存放信息
Response.Cookies["key"].Value="value";
//读取信息
string UserID=Response.Cookies["key"].Value;

④.ViewState对象

ViewState 常用于保存单个用户的状态信息,有效期等于页面的生存期。跟隐藏控件相似。viewstate是在本页面之内各函数间进行传值的 , 至于为什么要使用这种方法是因为在一个事件发生之后 , 页面可能会刷新 , 如果定义全局变量会被清零 , 所以要使用viewstate. ViewState容器可以保持大量的数据,但是必须谨慎使用,因为过多使用会影响应用程序的性能。所有Web服务器控件都使用ViewState在页面回发期音保存自己的状态信息。如果某个控件不需要在回发期间保存状态信息,最好关闭该对象的ViewState,避免不必要的资源浪费。通过给@Page指令添加“EnableViewState=false”属性可以禁止整个页面的ViewState。使用ViewState对象保存信息的代码如下。

  //存放信息
  ViewState["key"]="value";
  //读取信息
  string NameID=ViewState["nameID"].ToString();

⑤.Cache对象

Cache对象用于在HTTP请求间保存页面或数据。该对象的使用可以极大地提高整个应用程序的效率。常用于将频繁访问的大量服务器资源存储在内存中,当用户发出相同的请求后服务器不再次处理而是将Cache中保存的信息返回给用户,节省了服务器处理请求的时间。其生存期依赖于该应用程序的生存期。当重新启动应用程序时,将重新创建其Cache对象的实例。使用Cache对象保存信息的代码如下。

  //存放信息
  Cache["nameID"]="0001";
  //存放信息
  Cache.Insert("nameID","0001"1);
  //读取信息
  string NameID=Cache["nameID"].ToString();

⑥.隐藏域

Hidden控件是属于HTML类型的服务器控件,使用此控件可以实现隐藏域的功能。其实此控件和其它服务器控件的使用没有太大区别,只是它不会在用户端的浏览器中显示,始终处于隐藏状态。但是每次页面提交的时候,此控件和其它服务器控件一同提交到服务器端,因此在服务器端可以使用Value属性获取或保存一些数据信息。使用Hidden控件保存信息的代码如下。

 //存放信息
  Hidden.Value="0001";
  //获取信息
  string NameID=Hidden.Value;

⑦.查询字符串

查询字符串的方式是将要传递的值连接在URL后面,然后通过Response.Redirect方法实现客户端的重定向。这种方式可以实现在两个页面之间传递信息。由于URL的长度有一定的限制,因此不能传递太大的信息,加外安全性也不是很好。
传递信息如下。问号后面格式 key1=value1&key2=value2

Response.Redirect("List.aspx?nameID=0001&gradeID=002");
  //执行上面的语句后在IE地址栏显示的URL的代码如下。
  http://localhost/List.aspx?nameID=0001&grade=002
  //当跳转到List.aspx后,可以通过以下代码获得所传递的信息。
  string NameID.GradeID;

8.ajax原理

简述: Ajax的原理就是:通过javascript的方式,将前台数据通过xmlhttp对象传递到后台,后台在接收到请求后,将需要的结果,再传回到前台,这样就可以实现不需要页面的回发,页是数据实现来回传递,从页实现无刷新。

Ajax的原理简单来说,实际上就是通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。
这其中最关键的一步就是从服务器获得请求数据。要清楚这个过程和原理,我们必须对 XMLHttpRequest有所了解。

总结:我们可以看出,XMLHttpRequest对象完全用来向服务器发出一个请求的,它的作用也局限于此,但它的作用是整个ajax实现的关键,我们可以把服务器端看成一个数据接口,它返回的是一个纯文本流,当然,这个文本流可以是XML格式,可以是Html,可以是Javascript代码,也可以只是一个字符串。这时候,XMLHttpRequest向服务器端请求这个页面,服务器端将文本的结果写入页面,这和普通的web开发流程是一样的,不同的是,客户端在异步获取这个结果后,不是直接显示在页面,而是先由javascript来处理,然后再显示在页面。

9.请叙述属性与索引器的区别

属性 索引器
通过名称标识 通过签名标识
通过简单名称或成员访问来访问 通过元素访问来访问
可以为静态成员或实例成员 必须为实例成员
属性的get访问器没有参数 索引器的get访问器具有与索引器相同的形参表
属性的set访问器包含隐式value参数器 除了value参数外,索引的 set 访问器还具有与索引器相同的形参表

10.String类与StringBuilder类有什么区别?为啥在.Net类库中要同时存在这2个类?

如果要操作一个不断增长的字符串,尽量不用String类,改用StringBuilder类

String类原理:String类是一种传统的修改字符串的方式,它确实可以完成把一个字符串添加到另一个字符串上的工作没错,但是在.NET框架下,这个操作实在是划不来。因为系统先是把两个字符串写入内存,接着删除原来的String对象,然后创建一个String对象,并读取内存中的数据赋给该对象。这一来二去的,耗了不少时间。

StringBulider原理:而使用 System.Text命名空间下面的StringBuilder类就不是这样了,它提供的Append方法,能够在已有对象的原地进行字符串的修改,简单而且直接。

提醒:一般情况下觉察不到这二者效率的差异,但如果你要对某个字符串进行大量的添加操作,那么StringBuilder类所耗费的时间和 String类简直不是一个数量级的

11.浅谈C#中的枚举

枚举类型是一种的值类型,它用于声明一组命名的常数。

①.枚举的声明:枚举声明用于声明新的枚举类型。

 访问修辞符 enum 枚举名:基础类型
    {
        枚举成员
    }

基础类型必须能够表示该枚举中定义的所有枚举数值。枚举声明可以显式地声明 byte、sbyte、short、ushort、int、uint、long 或 ulong 类型作为对应的基础类型。没有显式地声明基础类型的枚举声明意味着所对应的基础类型是 int。

②.枚举成员

枚举成员是该枚举类型的命名常数。任意两个枚举成员不能具有相同的名称。每个枚举成员均具有相关联的常数值。此值的类型就是枚举的基础类型。每个枚举成员的常数值必须在该枚举的基础类型的范围之内。

    public enum TimeofDay:uint
        {
            Morning=-3,
            Afternoon=-2,
            Evening=-1
         }  

产生编译时错误,原因是常数值 -1、-2 和 –3 不在基础整型 uint 的范围内。

③.枚举成员默认值

在枚举类型中声明的第一个枚举成员它的默值为零。
以后的枚举成员值是将前一个枚举成员(按照文本顺序)的值加 1 得到的。这样增加后的值必须在该基础类型可表示的值的范围内;否则,会出现编译时错误。

    示例:
        public enum TimeofDay:uint
        {
            Morning,
            Afternoon,
            Evening
         }   

Morning的值为0,Afternoon的值为1,Evening的值为2。

④.为枚举成员显示赋值

允许多个枚举成员有相同的值。没有显示赋值的枚举成员的值,总是前一个枚举成员的值+1

  public enum Number
        {
            a=1,
            b,
            c=1,
            d
        }

b的值为2,d的值为2.
注意:以上枚举值都不能超过它的基础类型范围。否则会报错.

⑤.枚举类型与基础类型的转换

基础类型不能隐式转换为枚举类型; 枚举类型也不能隐式转换为基础类型

public enum Number
    {
        a,
        b,
        c,
        d
    }

    class Test
    {
        public static void Main()
        {
            int i=Number.a;//错误,要强制类型转换(int)Number.a
            Number n;
            n=2            //错误,要强制类型转换(Number)2
        }
    }

⑥.System.Enum类型

  1. System.Enum 类型是所有枚举类型的抽象基类,并且从 System.Enum 继承的成员在任何枚举类型中都可用。
  2. System.Enum 本身不是枚举类型。相反,它是一个类类型,所有枚举类型都是从它派生的。
  3. System.Enum 从类型 System.ValueType派生

⑦.使用枚举类型

 using System;
        public enum TimeofDay
        {
             Morning,
             Afternoon,
             Evening
        }   
        class Test 
        {
             static void WriteGreeting(TimeofDay timeofDay)
             {            
                  switch(timeofDay)
                  {
                       case TimeofDay.Morning:
                            Console.WriteLine("good morning");
                            break;
                       case TimeofDay.Afternoon:
                            Console.WriteLine("good afternoon");
                            break;
                       case TimeofDay.Evening:
                            Console.WriteLine("good evening");
                            break;
                  }
             }
             static void Main() 
             {
                  WriteGreeting(TimeofDay.Morning); 
                  WriteGreeting(TimeofDay.Evening);
                  WriteGreeting(TimeofDay.Afternoon);
             }
        }    

12.C#的New关键字的几种用法

①.new运算符:用于创建对象和调用构造函数。

  • 用于创建对象和调用构造函数 例:Class_Test MyClass=new Class_Test();
  • 也用于为值类型调用默认的构造函数
例:int myInt=new int();

myInt初始化为0,它是int类型的默认值。该语句的效果等同于:intmyInt=0;

  • 不能重载new运算符;
  • 如果new运算符分配内存失败,则它将引发OutOfMemoryException异常。

②.new修饰符

使用new修饰符显式隐藏从基类继承的成员。若要隐藏继承的成员,请使用相同名称在派生类中声明该成员,并用new修饰符修饰它。

请看下面的类:

public class MyClass 
{ 
public int x; 
public void Invoke(){} 
}

在派生类中用Invoke名称声明成员会隐藏基类中的Invoke方法,即:

public class MyDerivedC:MyClass 
{ 
new public void Invoke(){} 
}

但是,因为字段x不是通过类似名隐藏的,所以不会影响该字段。

通过继承隐藏名称采用下列形式之一:

1.引入类或结构中的常数、指定、属性或类型隐藏具有相同名称的所有基类成员。

2.引入类或结构中的方法隐藏基类中具有相同名称的属性、字段和类型。同时也隐藏具有相同签名的所有基类方法。

3.引入类或结构中的索引器将隐藏具有相同名称的所有基类索引器。

4.在同一成员上同时使用new和override是错误的。

注意:在不隐藏继承成员的声明中使用new修饰符将生成警告。

示例:在该例中,嵌套类MyClass隐藏了基类中具有相同名称的类。该例不仅说明了如何使用完全限定名访问隐藏类成员,同时也说明了如何使用new修饰符消除警告消息。

using System; 
public class MyBaseC
{
public class MyClass
{
public int x=200;
public int y;
}
}

public class MyDerivedC:MyBaseC
{
new public class MyClass //nestedtypehidingthebasetypemembers
{
public int x=100;
public int y;
public int z;
}
public static void Main()
{
//Creating object from the overlapping class:
MyClass S1=new MyClass();

//Creating object from the hidden class:
MyBaseC.MyClass S2=new MyBaseC.MyClass();

Console.WriteLine(S1.x);
Console.WriteLine(S2.x);
}

输出:
100
200

③.new约束:用于在泛型声明中约束可能用作类型参数的参数的类型。

转载于:https://www.cnblogs.com/renyiqiu/p/6475719.html

你可能感兴趣的:(面试,c#,javascript)