.NET理论知识 笔试准备 Day1

剩下十天左右时间,准备一下笔记题,也巩固一下基础知识。本想万一找不到合适的就在家脱产学到过年算了,后来想想确实没必要,就算年前不好找工作,无非是钱多钱少、路远路近的问题。实在找不到满意的,可以先凑合着工作两三个月,到转正时如果薪资什么的还达不到要求,到时年初比较好找工作。

对着书也看不出什么来,就在网上找找笔试面试题,简单的看看就算了,有些经典的有代表性的,随手记录下来。

前面一些是看园内”汉城节度使“去年四月的长文,后面是找一些其它资料。边看边记一些感想,其余查的资料就不一一标明出处了。

一、四种访问方式

private : 私有成员, 在类的内部才可以访问。
protected : 保护成员,该类内部和继承类中可以访问。
public : 公共成员,完全公开,没有访问限制。
internal: 当前程序集内可以访问。

其它都好理解,但这个程序集经常看到,具体说不上来。。。找到几篇资料,汇总一下,大概了解了。

protected限定的是只有在继承的子类中才可以访问,可以跨程序集   
internal限定的是只有在同一个程序集中才可以访问,可以跨类  
对于一些大型的项目,通常由很多个DLL文件组成,引用了这些DLL,就能访问DLL里面的类和类里面的方法。
比如,你写了一个记录日志的DLL,任何项目只要引用此DLL就能实现记录日志的功能,这个DLL文件的程序就是一个程序集。
情形一:比如MyCompany.MyProject.DAL里的若干个.cs文件,编译得到DAL程序集,而MyCompany.MyProject.BLL里的若干.cs文件,编译得到BLL程序集。
情形二:同样是MyCompany.MyProject.DAL空间里的,我可能有一个用于支持SQL Server的程序集DAL_SQLServer.dll,可能还有一个支持Oracle的程序集DAL_Oracle.dll。
命名空间与程序集没有一一对应的关系,关键还是看你的项目组织和输出

二、页面传值

1、可以通过url(增加&name=123&mail=abc.com之类)

2、可以通过cookie(先在这边存好cookie,到了新页面再取相应的cookie,但不安全)

3、可以通过Session(和cookie类似,区别在于cookie是浏览器端的,用js就能实现。而Session是服务器端的,如果是静态页面,要用ajax去服务端取。另外,高并发时Session可能会丢失)

4、通过Cache(用的比较少,和Session类似,但Sesseion是会话级别的,Cache的应用程序级的。一般重复的、公共的用Cache, 单独的、私有的用Session)

5、通过数据库

三、堆/栈、值/引用、结构/类、装箱/拆箱

这几种东西翻来翻去都是玩一个概念,就是两种基本存取类型,应该也是比较爱考的。

1、堆/栈、值/引用:

栈是编译期间就分配好的内存空间,因此你的代码中必须就栈的大小有明确的定义;局部值类型变量、值类型参数等都在栈内存中。
堆是程序运行期间动态分配的内存空间,你可以根据程序的运行情况确定要分配的堆内存的大小。
1.将一个值类型变量赋给另一个值类型变量时,将复制包含的值。引用类型变量的赋值只复制对对象的引用,而不复制对象本身。
2.值类型不可能派生出新的类型:所有的值类型均隐式派生自 System.ValueType。但与引用类型相同的是,结构也可以实现接口。
3.值类型不可能包含 null 值:然而,可空类型功能允许将 null 赋给值类型。
4.每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值。 

引用类型放在动态分配的内存(堆)中,只存取地址指针的取用。值类型是实实在在的值,放在规划好的内存空间(栈)中,存取的是具体的值。

奇葩点1:所有的值类型都继承自System.ValueType。而System.ValueType却是一个引用类型。

奇葩点2:String类型直接继承自Object,这使得它成为一个引用类型。一个字符串一旦被创建,我们就不能再将其变长、变短、或者改变其中的任何字符。

给字符串赋值其实相当于是重新生成一个新字符串,所以在大循环中,要用StringBuilder来代替String。

2、枚举/结构/类

Class可以被实例化,属于引用类型,是分配在内存的堆上的。类是引用传递的。
Struct属于值类型,是分配在内存的栈上的。结构体是复制传递的。Int32、Boolean等都属于结构体。

平时常用的都是类,结构比较少用。类肯定是引用的,记住了类,知道结构是和它反过来的就容易记了。

结构是值类型的,也就是不能被派生。

枚举也是值类型

枚举更像是列举对象值而不是类定义。
当对象值是明确的一系列值中的一个或多个的组合是可以定义该值系列的枚举。
以下情况可以考虑将类创建为结构:
(1)如果一个类其中的字段非常少,所有字段占用的内存总量不超过8、16字节;
(2)如果一个类中的字段都是值类型;

结构是跟类很相似的一种数据结构,其成员可以赋值,枚举只是一个有限的序列,是只读的

3、装箱/拆箱

装箱:从值类型接口转换到引用类型。
拆箱:从引用类型转换到值类型

装箱就是把一个值类型直接赋给object类型。

拆箱反过来,但要加个显式的强制转换,像(int) obj,转换失败还会报错

泛型显式指定类型,比装箱拆箱好,提高了性能和安全。 

四、.NET/C#

这也是老生常谈了。

.NET是一个平台/框架,号称是支持所有语言,全都可以拿到CLR来编译。但实际上除了自家的C++、VB、C#、F#之外,没见到其它公司什么语言整合进来,占大半江山的PHP和JAVA就更不可能放在.NET里面了(就算技术上行的通,人家也要百般阻挠啊,不然全都在你这玩了,其它人全喝西北风啊),所以还是个自娱自乐的货。

C#就是一些if/while/try等关键字语法。稍高级一些的各种方法,都是.NET框架的

五、GC垃圾回收

1、一个对象没有被任何对象引用时,就会被回收,但不是马上回收,而是由GC垃圾回收机制根据一定规则来处理,可手动运行GC.Collect()强制处理一下。

2、非托管资源GC不会回收,必须手工回收。如FileStream就要调用Dispose()回收,也可用using(){}来自动回收。

六、try/catch/finally

一个经典的坑就是try里面的return和finally

static void Main(string[] args){
    Console.Write(GetIt());
}

static int GetIt(){
    int i = 1;
    try{
        i++;
        Console.Write("a");
        return i;
    }
    finally{
        Console.Write("b");
        i++;
    }
}

输出的是ab2,因为finally会在try执行完后仍然执行,即使try有return,也会“卡”在那里等finally执行完,并且不影响return的值。

所以,通常在finally中执行Dispose(),这样,即可以保证不管报不报错都会释放资源,而且不影响return要用的值(会暂存起来),否则在return前就Dispose()了岂不是会报错?实际上并不会

七、out/ref

out只出不进,ref有进有出

out传进来前可以不赋初值,但不管在外面有没有赋值,在里面都访问不到,且离开时必须赋值

ref要有初值,在里面可以访问,不必强制赋值,如果有赋值会把值带到外面

八、静态/非静态

静态类的主要特性: 1:仅包含静态成员。 2:无法实例化。 3:是密封的。 4:不能包含实例构造函数。

静态成员
1:非静态类可以包含静态的方法、字段、属性或事件;
2:无论对一个类创建多少个实例,它的静态成员都只有一个副本;
3:静态方法和属性不能访问其包含类型中的非静态字段和事件,并且不能访问任何对象的实例变量; 
4:静态方法只能被重载,而不能被重写,因为静态方法不属于类的实例成员;
5:虽然字段不能声明为 static const,但 const字段的行为在本质上是静态的。这样的字段属于类,不属于类的实例。因此,可以同对待静态字段一样使用ClassName.MemberName 表示法来访问 const 字段;
6:C#不支持静态局部变量(在方法内部定义静态变量)。
1:静态类在内存中是一直有位置的;
2:非静态类在实例化后是在内存中是独立的,它的变量不会重复,在使用后会及时销毁。

九、先复制一段过来

 — 方法重载(method overload)

          — 定义:一个类中可以有一个以上的方法拥有相同的名称;

          — 不同的方法签名(signature)的信息组成也不同;

          — 方法签名的信息组成有:

             — 方法的名称、参数的数目、参数的数据类型和顺序;

             — 返回类型和形参的名称不是签名的一部分;

             — 形参的名称不是签名的一部分;

     — 扩展方法(C# 3.0)

          — 它允许编写和声明它的类之外的类关联的方法;

    — 它必须被声明为 static;

    — 它声明所在的类也必须被声明为 static ;

      — 它必须包含 this 作为它的第一个参数类型,并在后面跟着他所扩展的类的名称;

     — 外部方法(external method):

         — 它是在声明中没有实现的方法,现在常常是用C#之外的语言必须的;

         — 它使用 externa 修饰符标记,在类的声明中没有实现,而是被分号代替;

         — 声明和实现的连接时是依赖实现的,但常常使用DllImport特性完成;

     — 虚方法和覆写方法:

     — 它可以使基类的引用访问至派生类;

         — 派生类的方法和基类的方法有相同的签名和返回类型;

         — 基类的方法使用 virtual 标注;

         — 派生类的方法使用 override 标注;

         — 覆写和被覆写的方法必须有相同的可访问性;

         — 不能覆写 static 方法或非虚方法;

         — 覆写方法可以在继承的任何层次出现;

         — 方法、属性和索引,以及事件的,它们都可以声明为virtual和override;

     — 分部方法:

         — 在分部类中声明在两个部分中的方法,可以在(不)同一分部类中;

         — 定义声明,给出签名和返回类型,实现部分是一个分号;

         — 实现声明,给出签名和返回类型,还有语句块;

         — 两个声明都必须包含partial,直接放在void前;

         — 签名不能包含访问修饰符,这使分部方法是隐式私有的;

         — 返回类型必须是void;

         — 参数列表不能包含out参数;

         — 可以有定义部分而没有实现部分,反之,不行;

1、扩展方法,像js的原型一样,linq就有很多扩展方法,那些.where()之类都是

2、外部方法,一般用在引入dll(最近项目引入一些C++的dll,用DllImport,非常方便)

3、分部方法,相当于同一个方法,只是太长了放一个类里不方便,拆成两个一样的部份类(可以放在两个文件)

十、抽象方法(abstract)/虚方法(virtual)

抽象方法不能实例化,要子类必须强制性的覆盖它的方法 。虚方法提供了选择,可以覆盖可以不覆盖,继承基类中的虚方法。

抽象方法只能在抽象类中声明,虚方法则不是;
抽象方法必须在派生类中重写,虚方法则不必;
抽象方法不能声明方法实体,虚方法则可以。
抽象方法是只有定义、没有实际方法体的函数,它只能在抽象函数中出现,并且在子类中必须重写;
虚方法则有自己的函数体,已经提供了函数实现,但是允许在子类中重写或覆盖。
抽象方法是需要子类去实现的虚方法,是已经实现了,子类可以去覆盖,也可以不覆盖取决于需求. 
虚方法和抽象方法都可以供派生类重写,它们之间有什么区别呢?
1. 虚方法必须有实现部分,抽象方法没有提供实现部分,抽象方法是一种强制派生类覆盖的方法,否则派生类将不能被实例化。
2. 抽象方法只能在抽象类中声明,虚方法不是。其实如果类包含抽象方法,那么该类也是抽象的,也必须声明为抽象的。
3. 抽象方法必须在派生类中重写,这一点跟接口类似,虚方法不必。
virtual关键字只是明确标示此方法可以被重写, 其实它和一般的方法没有什么区别。相应的,sealed关键字标示此方法不可以被重写
使用了virtual修饰符后,不允许再有static、abstract或override修饰符。对于非虚的方法,无论被其所在类的实例调用,还是被这个类的派生类的实例调用,方法的执行方式不变。而对于虚方法,它的执行方式可以被派生类改变,这种改变是通过方法的重载来实现的。

抽象方法只能在抽象类中声明,这点和静态类/静态方法刚好反过来。

一旦类静态,则全部方法必须静态,反过来不必。一旦方法抽象,则整个类必须抽象,反过来不必。

注意,没有“虚类”这种说法。。。 

十一、抽象类/接口

 接口和抽象类的相似之处:
       1、不能实例化;
       2、包含未实现的方法声明;
       3、派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员);
1、抽象类可以有自己的方法(包括声明和定义),而接口只能声明,不能拥有定义。
2、派生类可以继承于多个接口,但只能继承于1个抽象类。
3、接口不能拥有修饰符,因为默认的成员都是public,但抽象类可以有。
4、派生类若不是抽象类,必须实现接口的所有方法,必须实现抽象类的所有抽象方法,但不需重写抽象类的成员方法。
5、接口只能继承于接口,但抽象类可以继承于抽象类和接口

接口:只声明,不能定义,不能实现。如果派生类是抽象类,不能实现。如果派生类不是抽象类,则必须实现接口的所有方法。

抽象类:可以声明、定义,不能实现。如果派生类是抽象类,不能实现。如果派生类不是抽象类,则其中的抽象方法必须被实现,其余的普通方法不用实现。

虚方法:可以声明、定义、必须有实现。可以被重写,但不强制。

慢慢对抽象类、虚方法、接口有了一定的认识。因为在项目中很少用到,所以感受不深。

十二、单例类

public FileManager
{
     private FileManager(){}
     public static FileManager Instance = new FileManager();
}

如果一个类始终只能创建一个实例,则这个类被称为单例类

/// <summary>
    /// 简单实现
    /// 类声明中使用sealed可防止其它类继承此类
    /// 私有构造函数禁止实例化该类
    /// 
    /// 缺点:这种方式的实现对于线程来说并不是安全的,因为在多线程的环境下有可能得到Singleton类的多个实例。如果同时有两个线程去判断(instance == null),并且得到的结果为真,这时两个线程都会创建类Singleton的实例,这样就违背了Singleton模式的原则。实际上在上述代码中,有可能在计算出表达式的值之前,对象实例已经被创建,但是内存模型并不能保证对象实例在第二个线程创建之前被发现
    /// 优点:
    /// 由于实例是在 Instance 属性方法内部创建的,因此类可以使用附加功能(例如,对子类进行实例化),即使它可能引入不想要的依赖性。          
    /// 直到对象要求产生一个实例才执行实例化;这种方法称为“惰性实例化”。惰性实例化避免了在应用程序启动时实例化不必要的 singleton。
    /// </summary>
    public sealed class Singleton
    {
        private static Singleton instance = null;
        
        private Singleton(){}

        public static Singleton Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new Singleton();
                }
                return instance;
            }
        }
    }
/// <summary>
    /// 静态初始化
    /// 在大多数情况下,静态初始化是在 .NET 中实现 Singleton 的首选方法。
    /// 该类标记为 sealed 以阻止发生派生,而派生可能会增加实例。此外,变量标记为 readonly,这意味着只能在静态初始化期间(此处显示的示例)或在类构造函数中分配变量。 
    /// 
    /// 该实现与前面的示例类似,不同之处在于它依赖公共语言运行库来初始化变量。它仍然可以用来解决 Singleton 模式试图解决的两个基本问题:全局访问和实例化控制。公共静态属性为访问实例提供了一个全局访问点。此外,由于构造函数是私有的,因此不能在类本身以外实例化 Singleton 类;因此,变量引用的是可以在系统中存在的唯一的实例。 由于 Singleton 实例被私有静态成员变量引用,因此在类首次被对 Instance 属性的调用所引用之前,不会发生实例化。
    /// 这种方法唯一的潜在缺点是,您对实例化机制的控制权较少。在 Design Patterns 形式中,您能够在实例化之前使用非默认的构造函数或执行其他任务。由于在此解决方案中由 .NET Framework 负责执行初始化,因此您没有这些选项。在大多数情况下,静态初始化是在 .NET 中实现 Singleton 的首选方法。
    /// </summary>
    public sealed class Singleton4
    { 
        private static readonly Singleton4 instance = new Singleton4();

        static Singleton4() { }

        private Singleton4() { }

        public static Singleton4 Instance
        {
            get
            {
                return instance;
            }
        }
    }

如果要线程安全,要加锁,就不深入研究了

原文地址:https://www.cnblogs.com/liuyouying/p/5067962.html