1. 简述 private、 protected、 public、 internal 修饰符的访问权限。
private :设置类或类的成员为私有,在类的内部才可以访问。有时要访问私有成员时,可通过get和set属性访问器读取或者修改。protected :保护成员,该类内部和继承类(子类)中可以访问。(即使子类和基类不在同一个程序集中)
public : 公共成员,完全公开,没有访问限制。须谨慎使用,滥用会影响类的封装性。
internal: 在同一命名空间(程序集)内可以访问。C#默认的类访问修饰符即为internal。(默认的修饰符,可缺省,最常用)
2. 描述sealed修饰符的作用
用于修饰类、实例方法和属性。sealed修饰类时,该类无法被继承,所以该类也被称为密封类。而abstract类(抽象类)必须被继承才有意义,所以二者是相互排斥,无法共用的。密封方法会重写基类的方法,sealed用于修饰实例被重写的虚方法或虚属性时,表示该方法或属性无法再被重写。
3、举例说明简单工厂模式的作用
在软件系统中,经常面临“一系列相互依赖对象”的创建工作,为了绕过常规对象的创建方式(即用new创建),利用工厂模式,提供一种“封装机制”来减少功能调用程序和“多系列具体对象创建工作”的耦合性。
简单工厂模式,在工厂类中封装创建对象的实现部分。在应用接口的程序中广泛应用。如下例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BaseFactory
{
//声明一个图书接口
interface IBook
{
//声明未实现的图书类型方法bookType()
void bookType();
}
//实现图书接口的A类图书
class BookA : IBook
{
public void bookType()
{
Console.WriteLine("A类书籍");
}
}
//实现图书接口的B类图书
class BookB : IBook
{
public void bookType()
{
Console.WriteLine("B类书籍");
}
}
//实例化各类图书对象的工厂
class PublishFactory
{
private IBook bka = new BookA(); //封装创建对象的实部分
private IBook bkb = new BookB();
public IBook getBook(int i)
{
switch (i)
{
case 1:
return bka;
case 2:
return bkb;
default:
return bka;
}
}
}
public class BasicFactory
{
static void Main(string[] args)
{
PublishFactory pf = new PublishFactory();
Console.Write("请输入编号(1或2):");
int i = int.Parse(Console.ReadLine());
//根据输入的不同参数获得不同的图书对象
IBook obj = pf.getBook(i);
obj.bookType();
}
}
}
接口反映了面向对象编程的一个特性,即多态,多态即通过相同的方法得到不同的实现。如上面的h1.getFav()和h2.getFav();
接口反映了面向对象编程的另个特性,即封装,使用者可以不清楚接口成员实现的细节。
解释: 接口类型obj可以引用不同类的实例,以致相同的bookType方法有不同的表现;使用者只知道传递数字来使用不同接口的不同功能,对内部实现一无所知。实现了更好的封装。
(此例已经够说明了)
运行结果:
为了处理更复杂的情况,可以将产品再次细分为多个类,用抽象类进行归纳,完成同类产品中共用代码的复用,工厂类也相应地分为多个类,用抽象类进行归纳。如下例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LxFactory
{
//声明一个人类接口
interface Ihuman
{
void getFav(); //声明未实现的爱好方法getFav()
string getStatus(); //声明未实现的身份状态(孩子,成年人)方法
}
//声明一个抽象类Children,并实现接口的部分方法
abstract class Children : Ihuman
{
protected string _status = "孩子";
//实现接口Ihuman中的getStatus()方法
public string getStatus()
{
return _status;
}
//将接口Ihuman中的getFav()方法映射到抽象方法
abstract public void getFav();
}
//声明一个抽象类Adult,并实现接口的部分方法
abstract class Adult : Ihuman
{
protected string _status = "成年人";
//实现接口Ihuman中的getStatus()方法
public string getStatus()
{
return _status;
}
//将接口Ihuman中的getFav()方法映射到抽象方法
abstract public void getFav();
}
//声明男孩类Boy,继承小孩类Children,重写爱好方法getFav()
class Boy : Children
{
public override void getFav()
{
Console.WriteLine("男孩,我喜欢游戏!");
}
}
//声明女孩类Girl,继承小孩类Children,重写爱好方法getFav()
class Girl : Children
{
public override void getFav()
{
Console.WriteLine("女孩,我喜欢布娃娃!");
}
}
//声明男人类Man,继承成人类Adult,重写爱好方法getFav()
class Man : Adult
{
public override void getFav()
{
Console.WriteLine("男人,我喜欢编程!");
}
}
//声明女人类Woman,继承成人类Adult,重写爱好方法getFav()
class Woman : Adult
{
public override void getFav()
{
Console.WriteLine("女人,我喜欢逛街!");
}
}
//声明一个抽象工厂,分别创建相应类的实例
abstract class HumanFactory
{
protected Ihuman h1 = new Boy();
protected Ihuman h2 = new Man();
protected Ihuman h3 = new Girl();
protected Ihuman h4 = new Woman();
//创建一个方法,根据不同输入返回一个接口的引用
abstract public Ihuman getHuman(int i);
}
//声明可以实例化各种类的工厂Factory1,继承类HumanFactory,重写抽象方法getHuman()
class Factory1 : HumanFactory
{
//根据不同输入获得不同对象所属接口的引用
public override Ihuman getHuman(int i)
{
switch (i)
{
case 1:
return h1;
case 2:
return h2;
default:
return h1;
}
}
}
//声明可以实例化各种类的工厂Factory1,继承类HumanFactory,重写抽象方法getHuman()
class Factory2 : HumanFactory
{
//根据不同输入获得不同对象所属接口的引用
public override Ihuman getHuman(int i)
{
switch (i)
{
case 1:
return h3;
case 2:
return h4;
default:
return h3;
}
}
}
public class LxFactory
{
static void Main(string[] args)
{
//使用工厂1
Console.WriteLine("男性中有两种人,请选择编号【1】男孩【2】男人:");
int input1 = Int32.Parse(Console.ReadLine());
Factory1 f1 = new Factory1();
Ihuman h1 = f1.getHuman(input1);
h1.getFav();
Console.WriteLine("我的身份是:{0}", h1.getStatus());
//使用工厂2
Console.WriteLine("女性中有两种人,请选择编号【1】女孩【2】女人:");
int input2 = Int32.Parse(Console.ReadLine());
Factory2 f2 = new Factory2();
Ihuman h2 = f2.getHuman(input2);
h2.getFav();
Console.WriteLine("我的身份是:{0}", h2.getStatus());
}
}
}
运行结果:
4、访问关键字this和base有什么作用
this用于引用类的当前实例(this仅限于构造函数和方法成员中使用,静态成员与实例无关,所以静态成员中不能用this)
base用于派生类访问基类成员(基类被重写的方法和基类的构造函数)
5、简述程序集和应用程序域
程序集
一个.NET应用程序可以由多个程序集拼装而成的。程序集,简单来说,就是一个以公共语言运行库(CLR)为宿主的、版本化的、自描述的二进制文件。尽管显示中.NET程序集和以往Win32二进制文件(包括遗留的COM服务对象)的文件扩展名(*.exe或*.dll)完全相同,但是两者的内部构成几乎完全不同。
程序集可以促进代码重用、确定类型边界、可版本化的单元、自描述的、可配置的。
n CIL代码
程序集的核心部分包含CIL代码,这些CIL代码是独立于平台和CPU的中间语言。运行时,程序集内部的CIL代码才被编译成特定平台和CPU的指令。
n 类型元数据
元数据完整的描述了程序集内含另行和引用外部类型的格式。
n 程序集清单
详细记录了程序集中的每一个模块,构建程序集的版本以及该程序集引用的所有外部程序集。
n 可选的嵌入资源
NET平台下,程序集并没有直接加载进进程中(传统的Win32程序是直接承载的)。.NET可执行程序(程序集)承载在进程的一个逻辑分区中,术语称应用程序域(简称AppDomain)。一个进程可以拥有多个应用程序域,应用程序域的全部目的就是提供隔离性,相比较与传统的:(代码, AppDomain.currentDomain)
1.应用程序域是.NET平台操作系统独立性的关键特性。这种逻辑分区将不同操作系统表现加载可执行程序的差异抽象化了。
2.和一个完整的进程相比,应用程序域的CPU和内存占用要小的多。
3.应用程序域为承载的应用程序提供了深度的隔离。一个失败,其他不会失败。
单个进程可以承载多个应用程序域,每个程序域都和该进程的其他程序域隔离开来,无法相互访问。在线程启动的时候,CLR将自动创建这个特定的应用程序域(默认应用程序域)。然后根据需要创建其他程序域。
![](https://i-blog.csdnimg.cn/blog_migrate/0820d0acbf321dafba60b204af20818a.jpeg)
两边都有mscorlib.dll(程序集),因为所有关键程序集会被CLR自动加载到每一个应用程序域中。
6、简述结构和类的区别
答:(1)结构是值类型(传递的是数据的副本),类是引用类型。所以结构的实例化可以不用new运算符。
(值类型变量一旦超出作用域即被销毁,而应用类型由内存垃圾处理机制完成收集)
(2)结构不能声明无参数的构造函数(默认构造函数)和析构函数。类可以同时有构造函数和析构函数。
(3)结构不支持继承,也无法派生其他的结构或类,(但结构可以实现接口)。而类支持继承。
7、C#函数的参数 修饰符(out、params、ref)的作用
(1)无修饰符,表示按值传递
(2)out修饰,eg. 函数 static void equal(out int a,out int b,out int c)
{ a=2; b=a*100; c=b*100; Console.WriteLine("a、b、c的值分别为:{0}、{1}、{2}",a,b,c);}
表示按引用 传递参数,实际参数(调用的函数里的参数)传递前不需要赋初值,函数(定义的函数)必须将其赋值。也可以看出out能用于一次调用返回多个值
(3)params修饰,表示形式参数 可以为不确定个数的一组实际参数,这些实际参数必须具有相同的数据类型。
void text(params string[] txt){}
(4)ref修饰,类似out参数。也表示按引用 传递参数,不过实际参数传递前需要赋初值。
8、如何使用C#的对象构造器
这是一种新的构造并初始化对象的方法,使用时只需将初始化成员的语句用逗号分隔,位置紧跟在构造函数后面。
eg. Snake a=new Snake("眼镜蛇"){nm="蟒蛇",Len=10,Wei=20 };
后面大括号就是对象构造器。。列出了 对象a 的初始化值。 这种方式非常灵活的初始化对象成员,无需定义过多的重载构造函数。
9、如何判断类型实现了某个接口 (as/is关键词)
ClassA a=new ClassA();
var msg=a as ITest;
class ClassA:ITest{ 类体}
if(msg==null)
{"未实现"}
else {"实现了"}
或者用 a is ITest
10、数组列表和数组有什么区别
非常相似,但是数组列表(即ArrayList类, 位于System.Collection下)的容量可以动态变化;有点类似StringBuilder类。。
ArrayList.Add(); ArrayList.Insert(); ArrayList.AddRange();-----
数组(即Array类,位于System下,)在初始化时要指定容量,并且指定后无法更改。