总结

C# 专栏收录该内容
28 篇文章 0 订阅

最近项目组不太忙,最近一下一些自己的积累,包含C#基本技术知识,linq 算法 设计模式 数据结构 数据库 网络安全

基础知识梳理图:

1.迭代器在其实就是指针,读取集合或者数组中的一个值,读完以后又指向下一条数据。 

2.迭代器是很好用的一个东西,以java为例,在遍历list或者map等对象的时候,效率比较高,而且能实现一些for循环不能实现的功能。例如remove

做强签名的assembly与不做强签名的assembly有什么不同?
强签名的程序集可以做成com,而不做强签名的就不行,同样强签名程序集可以安装到GAC中,而不做强签名的确不能。

请解释转发与跳转的区别 
转发就是服务端的跳转A页面提交数据到B页面,B页面进行处理然后从服务端跳转到其它页面 

跳转就是指客户端的跳转

委托(delegate):可以理解为一种协议,碰到一件事,你需要让别人来帮你做,为什么说委托像一个协议呐,因为你不想把事情搞砸,所以你委托的这个人做事情需要给他制定一个标准,在C#中就是给委托的对象定义好签名,参数有几个,分别什么类型,委托方法返回什么或者不返回。

例如:

public delegate int MyDelegate(int x, int y);

    class Delegate_Demo {

        static voidMain(string[] args) {

            Helper helper =new Helper();//实例化被委托者

            MyDelegate myDele = newMyDelegate(helper.Add); //创建委托对象

            int sum =myDele(1, 2);

           Console.WriteLine(sum);

        }

    }

    class Helper {

        public Helper() {}

        public int Add(intnum1, int num2) {

            return num1 +num2;

        }

}

这个例子中申明一个委托,告诉被委托者,你要这么做,我给你两个整数,你帮我计算他们的和,怎么计算我不管,计算后把结果给我即可

事件(event):时间是特殊的委托

在某件事发生时,一个对象可以通过事件通知另一个对象,比如页面按钮事件,就事件来说

关键点是什么时间,让谁去做。

例如:

publicdelegateintEventMyAdd(int a, int b);

   publicclassEventStudy

   {

       ///<summary>

       ///用关键字event和相关委托申明这个事件

       ///</summary>

       staticeventEventMyAdd eventmyadd;

       publicint Add(int a,int b)

       {

            eventmyadd +=newEventHelp().EventAdd;

            return eventmyadd(a, b);

       }

   }

   classEventHelp

   {

       public EventHelp()

       {

       }

       publicint EventAdd(int a, int b)

       {

            return a + b;

       }

   }

注:事件是一种特殊的委托。

asp.net button的客户端事件是如何映射成服务器端事件的?

谈谈ajax原理的了解程度以及目前业界流行的ajax框架的熟悉程度;

(https://www.cnblogs.com/mingmingruyuedlut/archive/2011/10/18/2216553.html)


datagrid.datasouse可以连接什么数据源[dataset,datatable,dataview]

dataset,datatable,dataview , IList 

 



CTSCLSCLR分别作何解释? 
答:CTS:通用语言系统。CLS:通用语言规范。CLR:公共语言运行库。

CLR框架图

 

什么是受管制的代码? 
答:unsafe:非托管代码。不经过CLR运行。

.NET项目都是运行在.NET Framework上的托管代码,这个道理和JAVA虚拟机的机制是类似的。 

受托管的代码不能直接写内存,是安全的,而非托管代码是非安全代码,可以使用指针操作内存  
一般的项目使用托管代码都行了,也就是说在程序里面不需要用到非安全代码。 
对于一些对速度要求高的部分功能可以考虑使用非安全代码,使用指针等读写内存,而对于真个项目来说还是受托管的安全代码。 

托管的代码就是把有关内存管理(内存申请,内存释放,垃圾回收之类的)全部都是.netCLR来管理,就是说使用托管的代码把底层的一些操作都封装起来了,不能直接进行内存的读取之类的和硬件相关的操作 

优点就是比较安全,不会出现诸如内存泄露之类的问题,缺点也很明显,不能直接读取内存,性能上会有损失,使用起来有时也不够灵活。 
非托管的刚好相反,可以直接进行硬件操作,性能比较高,但是对开发人员的要求也比较高 

什么是强类型系统? 
答:RTTI:类型识别系统。

强类型系统(RTTI):(Run-Time TypeIdentification)运行时类型识别

强类型系统就是对每一个数据都有类型的限制。

int sum=0;string str="";

C#C++都是强类型语言,变量必须先声明才能使用。

javascript则是弱类型语言。

什么是code-behind技术 
codebehind
是指代码和用户界面分开 
aspx and cs

PDB是什么东西? 在调试中它应该放在哪里?

PDB是用于保存调试和项目状态信息的文件,在debug的时候将产生pdb文件,调试的时候应该放在和对应应用程序集相同目录。

.net中,配件的意思是? 
答:程序集。(中间语言,源数据,资源,装配清单)

调用Assembly.Load算静态引用还是动态引用?

动态

从概念上阐述前期绑定(early-binding)和后期绑定(late-binding)的区别?

这个就像是强弱类型的比较相似,前期绑定是在编译的时候就确定了要绑定的数据,而后期绑定是在运行的时候才填充数据。所以前期绑定如果失败,会在编译时报编译错误,而后期绑定失败只有在运行时的时候才发生。

列举一下你所了解的XML技术及其应用 
答:保存配置,站与站之间的交流,WEBSERVICE,以及与数据库的数据交互等地方都要用它


引用类型 / 值类型,装箱 / 拆箱问题


栈(Stack)由系统管理生存期,存储代码执行和调用路径,执行或调用完毕即从栈中清除; 

堆(Heap)中保存值和对象,调用完毕之后依然存在,由垃圾回收器查找栈中有无指向该值或对象的引用,无则从堆中删除。



什么是Interface?它与Abstract Class有什么区别?

接口(Interface)是用来定义行为规范的,不会有具体实现,而抽象类除定义行为规范外,可以有部分实现,但一个类能实现多个接口,但只能继承一个父类  

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

什么是抽象类(abstract class)? 
一种不可以被实例化的类。抽象类中一般含有抽象方法,当然也可有具体实现。继承类只有实现过所有抽象类的抽象方法后才能被实例化。

何时必须声明一个类为抽象类? 
当这个类中包含抽象方法时,或是该类并没有完全实现父类的抽象方法时。

口(interface)是什么? 
只含有共有抽象方法(public abstract method)的类。这些方法必须在子类中被实现。

为什么不能指定接口中方法的修饰符? 
接口中的方法用来定义对象之间通信的契约,指定接口中的方法为私有或保护没有意义。他们默认为公有方法。

可以继承多个接口么? 
当然。

那么如果这些接口中有重复的方法名称呢? 
这种情况中你可以决定如何实现。当然需要特别得小心。但是在编译环节是没有问题的。

接口和抽象类的区别是什么? 
接口中所有方法必须是抽象的,并且不能指定方法的访问修饰符。抽象类中可以有方法的实现,也可以指定方法的访问修饰符。

抽象类是指:一个类不与具体的事物相联系,而只是表达一种抽象的概念,仅仅是作为其派生类的一个基类。

抽象类是一种特殊类,使用abstract声明

抽象类不可被实例化

抽象类可以继承一个抽象类

抽象方法只能存在于抽象类中,可以和虚方法一样,在派生类中重写。

抽象类的存在就是被继承用的,因此不允许被密封(即不能使用sealed修饰)

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

不能直接实例化接口。 
接口不包含方法的实现。 
接口、类和结构可从多个接口继承。但是C# 只支持单继承:类只能从一个基类继承实现。 
类定义可在不同的源文件之间进行拆分。 
同: 
接口、类和结构可从多个接口继承。 
接口类似于抽象基类:继承接口的任何非抽象类型都必须实现接口的所有成员。 
接口可以包含事件、索引器、方法和属性。 
一个类可以实现多个接口。

asp.net的管道模型

 System.Web的命名空间中处理HTTP请求主要使用管道模型。管道模型如下图所示。在管道模型开始运行前,首先HTTP的请求被传到HttpRuntime类的一个实例中,然后这个实例对象检查请求并找到被接受的那个应用程序。接下来,管道模型就使用HttpApplicationFactory对象来创建一个HttpApplication对象来处理这个请求,一个HttpApplication可以包含一系列HTTP module对象。其中最重要的就是HttpModuleHttpHandler


HttpModule,可以看做是一个拦截器,给我们在特定的事件处理请求的机会。HttpModule有很多应用,例如,我们要在每个请求的页面事件前加载Session数据,那么就用到SessionModule等等;asp.net4.0提供了路由机制也是建立在一个UrlRouteModule上面的,它在请求映射到具体程序前拦截,然后重新映射。MVC又是建立在路由机制的基础上的。HttpHandler,可以看做一个处理器,它负责处理请求,输出数据。aspx,ashx或者说实现了IHttpHandler的都是HttpHandler。管道模型使用一个HttpContext对象去描述声明request/response信息。这个对象在HttpApplicationhandler之间来回传递。HttpContext对象通过属性来描述requestresponse信息。下图展示了部分HttpContext类常用的属性。

上图可以请求到达IIS,经过HttpApplication工厂得到一个HttpApplication,创建一个HttpContext上下文,然后就会进入Http Pipeline

HTTP请求---> HTTP.SYS程序处理HTTP请求,判断请求是否在服务器的缓存区----> 如果缓存没有,则进入IIS ---->  IIS首先判断是何种类型的文件,根据文件调用相应的程序---->如果是ASP.NET程序,则调用工作进程w3wp.exeIIS7以上)装载asp_isapi.dll程序集----->asp_isapi.dll加载CLR环境,创建APPDomain,创建HOSTEnviment对象----->创建ISAPIRunTime对象----->ISAPIRuntimeHTTPWorkRequest对象传递给---->HTTPRuntime---->HttpApplication Factory-->HttpApplication-->HttpModule-->HttpHandler Factory-->HttpHandler->HttpHandler.ProcessRequest()。

为什么asp.net控件能够保持住状态

ViewState

什么是Viewstate?它有什么作用?

ViewState用来保存页面状态,就是说提交之后我们还可以看到文本框里面的内容就是ViewState保存的功劳。ViewState只维护当前页面的状态,不同页面之间不能共享,Session可以。ViewState你可以理解为一个隐藏控件。

ASP.net的身份验证方式有哪些?分别是什么原理? 
答:Windwos(默认)      IIS...From(窗体)      用帐户....Passport(密钥)

ASP.NET页面之间传递值的几种方式

QueryString,Session,Cookies,Application,Server.Transfer。

Response.Redirect和Server.Transfer 
请求的过程: 
1)
浏览器aspx文件请求--->服务器执行--->遇到Response.Redirect语句->服务器发送Response.Redirect后面的地址给客户机端的浏览器--->浏览器请求执行新的地址 
2)浏览器aspx文件请求->服务器执行->遇到Server.Transfer语句->服务器转向新的文件 
可以见Server.Transfer比Response.Redirect少了一次服务器发送回来和客户端再请求的过程. 
跳转对象: 
1)Response.Redirect可以切换到任何存在的网页。 
2)Server.Transfer只能切换到同目录或者子目录的网页. 
数据保密: 
1、Response.Redirect后地址会变成跳转后的页面地址。 
2、Server.Transfer后地址不变,隐藏了新网页的地址及附带在地址后边的参数值。具有数据保密功能。 
传递的数据量(网址后附带的参数): 
1、Response.Redirect能够传递的数据以2KB(也就是地址栏中地址的最大的长度)为限。 
2、传递的数据超过2KB时,务必使用Server.Transfer。

const和readonly有什么区别? 
const
关键字用来声明编译时常量,readonly用来声明运行时常量。

用sealed修饰的类有什么特点 
sealed
修饰符用于防止从所修饰的类派生出其它类。如果一个密封类被指定为其他类的基类,则会发生编译时错误。 密封类不能同时为抽象类。sealed 修饰符主要用于防止非有意的派生,但是它还能促使某些运行时优化。具体说来,由于密封类永远不会有任何派生类,所以对密封类的实例的虚拟函数成员的调用可以转换为非虚拟调用来处理。 

解释virtual、sealed、override和abstract的区别 
virtual
申明虚方法的关键字,说明该方法可以被重写 
sealed
说明该类不可被继承 
override重写基类的方法 
abstract申明抽象类和抽象方法的关键字,抽象方法不提供实现,由子类实现,抽象类不可实例化。

C# ref与out区别: 

1、使用ref型参数时,传入的参数必须先被初始化。对out而言,必须在方法中对其完成初始化。

2、使用ref和out时,在方法的参数和执行方法时,都要加Ref或Out关键字。以满足匹配。

3、out适合用在需要retrun多个返回值的地方,而ref则用在需要被调用的方法修改调用者的引用的时候。

new有几种用法 

第一种 创建对象、调用构造函数,这就不用讲了ClassA A=new ClassA();

第二种是作为修饰符,显示隐藏继承于基类的继承成员 

第三种是用在泛型中添加类型的约束(new()约束泛型有new()约束才泛型可以实例化)

class Test<T> where T : new()//定义类型T的约束,表示T类型必须有不带参数的构造函数

   {

       public T GetItem()

       {

           // return T();

           return new T();//如果不添加new()约束,编译错误:变量类型“T”没有 new() 约束,因此无法创建该类型的实例

           //想一下,T类型不知道,编译器不知道分配多大的空间,所以会通过反射技术实现

       }

   }

   class TClass

   {

       private int a;

       public TClass()  //如果不添加无参构造函数,编译错误:TClass必须是具有公共的无参数构造函数的非抽象类型,才能用作泛型类型或方法“A.Test<T>”中的参数“T”

       {

       }

       public TClass(int a)

       {

           this.a = a;

       }

   }

   class Program

   {

       static void Main(string[] args)

       {

           Test<TClass> test = new Test<TClass>();

           Console.ReadLine();

       }

    }


虚函数的用法  
1)virtual
指明一成员函数为虚函数,而virtual仅用于类的定义里,在类外可不加此关键字. 
2)一个类的成员函数被定义为虚函数时,子类该函数仍保持虚函数特征。 
3)子类覆盖此函数时,定义里可不加virtual关键字,但函数声明要和基类的完全一致!且此声明是必须的. 
4)不是纯虚函数时,父类的虚函数必须要实现; 而若将父类的虚函数设定为纯虚函数时,子类必需要覆盖之而且必须要实现之! 

 继承:

如果子类方法的方法名和基类的方法名相同时,系统将隐藏基类同名方法,自动调用子类的同名方法                

派生类会继承基类所有的成员,但是不能显示调用基类的私有成员                

在派生类中不可以调用基类的是有成员,如num1,num2,但是可以实现调用基类方法(这个基类的方法可以操作私有变量) 

不能重写非虚方法或静态方法,重写的基方法必须是 virtual abstract override

override声明不能更改virtual方法的可访问性

override方法和virtual方法必须具有相同的访问级别修饰符                

不能使用修饰符 new static virtual abstract来修改override方法 

重写属性声明必须指定与继承属性完全相同的访问修饰符类型和名称并且被重写的属性必须是 virtual abstract override

标记允许被重写修饰静态方法中不允许使用virtual关键字,成员变量允许使用virtual关键字。

重写和隐藏的区别:

隐藏(new关键字)是给子类的同名方法分配新的内存空间

重写(override关键字)是子类的同名方法放在基类同名方法的原来所在位置,基类的同名方法位置向后移。

父类的引用指向子类的实例(Testts = new Test2();)

1.  父类的引用指向子类的实例(调用的是子类的方法)

2.  父类的引用只认识父类的方法不认识子类的新方法可以用来调用被子类覆盖的父类的方法 

3.  父类的引用依然到父类方法位置去调用如果基类方法被声明为virtual并且在子类中被override结果访问到的是被子类override的方法

你对泛型了解吗?简单说明一下泛型的有什么好处?

泛型:通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用 
好处是—类型安全和减少装箱、拆箱。提高性能、类型安全和质量,减少重复性的编程任务 

无需类型转换

C#支持多重继承吗? 
不支持。可以用接口来实现

私有成员会被继承么? 
会,但是不能被访问。所以看上去他们似乎是不能被继承的,但实际上确实被继承了。 

C#提供一个默认的无参构造函数,当我实现了另外一个有一个参数的构造函数时,还想保留这个无参数的构造函数。这样我应该写几个构造函数? 
答:两个,一旦你实现了一个构造函数,C#就不会再提供默认的构造函数了,所以需要手动实现那个无参构造函数。

能用foreach遍历访问的对象需要实现IEnumerable接口或声明GetEnumerator方法的类型。

IEnumerable.GetEnumerator:返回循环访问集合的枚举数。

IEnumerable 接口:公开枚举数,该枚举数支持在非泛型集合上进行简单迭代

C#可否对内存进行直接的操作?

可以使用指针

unsafe 
{
int* pi;
int x = 1;
pi = &x;
System.Console.WriteLine("Value of x is:" + *pi);
}

编译后执行将会输出:
Value of x is: 1 

.net下,.net引用了垃圾回收(GC)功能,它替代了程序员不过在C#中,不能直接实现Finalize方法,而是在析构函数中调用基类的Finalize()方法。

DateTime是否可以为null? 

不能,因为其为Struct类型,而结构属于值类型,值类型不能为null,只有引用类型才能被赋值null

DateTime.Parse(myString); 这行代码有什么问题?

有问题,当myString不能满足时间格式要求的时候,会引发异常,建议使用DateTime.TryParse() 

为什么不提倡catch(Exception) 
try..catch在出现异常的时候影响性能; 应该捕获更具体得异常,比如IOExeception,OutOfMemoryException 

catch(Exception e){throw e;}catch(Exception e){throw;}的区别

将发生的异常对象抛出,另一个只是抛出异常,并没有抛出原异常对象) 

errorexception区别:

error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。

exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。

GETPOST的区别

1.FORM提交的时候,如果不指定Method,则默认为GET请求,Form中提交的数据将会附加在url之后,以?分开与url分开。字母数字字符原样发送,但空格转换为“+“号,其它符号转换为%XX,其中XX为该符号以16进制表示的ASCII(或ISOLatin-1)值。GET请求请提交的数据放置在HTTP请求协议头中,而POST提交的数据则放在实体数据中;

2.在使用 POST 方法的情况下,传输数据时不会将数据作为 URL 的一部分;它们会作为一个独立的实体来传输。因此,POST 方法更安全,你也可以用这个方法传输更多的数据。而且用 POST 传输的数据不一定要是文本,用 GET 方法传输的却一定要是文本。

1get是从服务器上获取数据,post是向服务器传送数据。

1)在客户端,Get方式在通过URL提交数据,数据在URL中可以看到;POST方式,数据放置在HTMLHEADER内提交。

2)对于get方式,服务器端用Request.QueryString获取变量的值,对于post方式,服务器端用Request.Form获取提交的数据。

2GET方式提交的数据最多只能有1024字节,而POST则没有此限制。

3)安全性问题。正如在(1)中提到,使用 Get 的时候,参数会显示在地址栏上,而 Post 不会。所以,如果这些数据是中文数据而且是非敏感数据,那么使用 get;如果用户输入的数据不是中文字符而且包含敏感数据,那么还是使用 post为好。

反射和序列化

反射是在外部通过反射可以知晓内部的东西像我们引用一个程序集,可以在咱们自己的引用相应命名空间可以访问程序集里面的实例方法。

序列化:把一个对象转化为其他格式。常用的是把对象转化为json传递给客户端。

XmlSerializer是如何工作的?使用这个类的进程需要什么ACL权限?

XmlSerializer是将对象的属性和字段进行序列化和反序列化的,序列化成为xml数据,反序列化再将xml转换成对象。应该至少需要ACL权限中的读权限。

XmlSerializer使用的针对属性的模式有什么好处?解决了什么问题?

只序列化有用的数据,而不是序列化整个对象。实现没必要的数据冗余。

提升序列化时的性能。

<%# %> <% %> 有什么区别?

<%# %>表示绑定的数据源 

<% %>是服务器端代码块常量 

BindEval函数的区别

绑定表达式 
    <%# Eval("
字段名") %> 
    <%# Bind("
字段名") %> 
Eval
单向绑定:数据是只读的 
Bind
双向绑定:数据可以更改,并返回服务器端,服务器可以处理更改后的数据,如存入数据库
当对次表达式操作时候,必须用Eval <%#Eval("字段").ToString().Trim()%> 
绑定控件的属性时要用Bind,而Eval则是其它一些。 
例如:<asp:TextBoxID="First" RunAt="Server" Text='<%#Bind("FirstName") %>' /> 
例如:<td><%#Eval("ProductID") %></td>

使用eval("("+data+")")可以将其转换为json对象,和JSON.parse的功能一样。

String类与StringBuilder类有什么区别?为什么在.Net类库中要同时存在这2个类?(简答)

stringBuilderstring更节约内存,所以stringBuilder更快

String 对象是不可改变的。每次使用 System.String 类中的方法之一或进行运算时(如赋值、拼接等)时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。而 StringBuilder 则不会。在需要对字符串执行重复修改的情况下,与创建新的String 对象相关的系统开销可能会非常昂贵。如果要修改字符串而不创建新的对象,则可以使用System.Text.StringBuilder 类。例如,当在一个循环中将许多字符串连接在一起时,使用StringBuilder 类可以提升性能。


传入某个属性的SET方法的隐含参数的名称是什么? 
答:value,它的类型和属性所声明的类型相同。 

string是值类型还是引用类型?

引用类型 原因:string类型是只读的引用类型,修改string对象会创建新的实例

提高.NET的性能 
1
使用异步方式调用Web服务和远程对象

只要有可能就要避免在请求的处理过程中对Web服务和远程对象的同步调用,因为它占用的是的ASP.NET 线程池中的工作线程,这将直接影响Web服务器响应其它请求的能力。

2 使用适当的Caching策略来提高性能

3 判断字符串,不要用""比较。

//避免 
if(strABC!=null && strABC!="") 
{}

//推荐 
if(!strABC.IsNullOrEmpty) 
{}

4 页面优化

5 用完马上关闭数据库连接 
6
尽量使用存储过程,并优化查询语句 
7
只读数据访问用SqlDataReader,不要使用DataSet

ASP.Net页面生命周期 
每个页面的生命周期为用户的每一次访问,也就是说每一次客户端与服务器之间的一个往返过程.全局变量的生命周期在此之间.

1. Page_Init(); 
2. Load ViewState and Postback data; 
3. Page_Load(); 
4. Handle control events; 
5. Page_PreRender(); 
6. Page_Render(); 
7. Unload event; 

8. Dispose method called; 

stringstrTmp = "abcdefg某某某"; 
int i= System.Text.Encoding.Default.GetBytes(strTmp).Length; 
int j= strTmp.Length; 
以上代码执行完后,i=j= 
答:i=13,j=10

string str= " a1我是。";

sum = System.Text.Encoding.UTF8.GetBytes(str).Length;(11

sum = Encoding.Default.GetBytes(str).Length;(8

sum = Encoding.ASCII.GetBytes(str).Length;(5

sum = Encoding.Unicode.GetBytes(str).Length;(10一个字母,数字,汉字都为两个字节。

sum = str.Length;(5

默认编码:一个汉字等于两个字节,

utf8:一个汉字3个字节,

ASCII:一个汉字一个字节。网上说:一个中文汉字占两个字节的空间

注:中文标点符号和汉字一样

什么叫圈复杂度(cyclomatic complexity)?为什么它很重要?

一种代码复杂度的衡量标准。圈复杂度大说明程序代码可能质量低且难于测试和维护,根据经验,程序的可能错误和高的圈复杂度有着很大关系。


何时使用Assembly.LoadFrom?何时使用Assembly.LoadFile 
呵呵,这个比较有意思,相比LoadFileLoadFrom则显得不地道,因为它娶媳妇的时候,是让人家穿上嫁妆,坐上马车,还得带着人家的妹妹来,:)用它加载的是程序集,这就要求同时将此程序集所依赖的程序集加载进来。而LoadFile就地道的多,它是加载程序集文件的内容,只将传入参数的文件加载,不考虑程序集依赖,但如果有相同实现,但位置不同的文件用LoadFrom是不能同时加载进来的,而LoadFile却可以。由于LoadFile加载的是文件,所以调用它之后,可能因为缺少必要的依赖造成无法被执行。 

单个TCP/IP端口上能够侦听多少个进程?

可以为多个,多个为端口复用
看下面代码  端口复用 
Socket socket1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp); 
Socket socket2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
socket1.Bind(newIPEndPoint(IPAddress.Parse("127.0.0.1"),8235)); 
socket1.Listen(10); 
socket2.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.ReuseAddress, true); 
socket2.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8235)); 
socket2.Listen(10); 
Console.Read(); 

什么是GAC?它解决了什么问题?

每一个CLR(Common Language Runtime)所在的计算机都有一个全局程序集缓存(GlobalAssembly Cache,GAC)

作用是可以存放一些有很多程序都要用到的公共Assembly,例如System.DataSystem.Windows.Forms等等。这样,很多程序就可以从GAC里面取得Assembly,而不需要再把所有要用到的Assembly都拷贝到应用程序的执行目录下面。举例而言,如果没有GAC,那么势必每个WinForm程序的目录下就都要从C:/WINDOWS/Microsoft.NET/Framework/v1.0.3705下面拷贝一份System.Windows.Forms.dll,这样显然不如都从GAC里面取用方便,也有利于Assembly的升级和版本控制。   

PID是什么?在做系统的故障排除时如何使用它?

PID =Process Identifier, 是一个全局唯一的用来标识进程的整数。在多任务系统中,可用来诊断系统中发生错误的进程。

Windows上的单个进程所能访问的最大内存量是多少?它与系统的最大虚拟内存一样吗?这对于系统设计有什么影响?

Windows使用一个系统:虚拟寻址系统。该系统把程序可用的内存地址映射到硬件内存中的实际地址上,这些任务完全有Windows后台管理,其实际结果是32位处理器上的每个进程都可以使用4GB的内存------无论计算机上有多少硬盘空间(在64位处理器上这个数值会更大)。这个4GB内存实际上包含了程序的所有部分------包括可执行代码,代码加载的所有dll,以及程序运行时使用的所有变量的内容。这个4GB内存称为虚拟地址空间,或虚拟内存。

这个需要针对硬件平台,公式为单个进程能访问的最大内存量=2的处理器位数次方/2,比如通常情况下,32位处理器下,单个进程所能访问的最大内存量为:2^32 /2 = 2G 

单个进程能访问的最大内存量是最大虚拟内存的1/2,因为要分配给操作系统一半虚拟内存。


简述你对XML WebService的原理的认识? 
答:利用SOAP(简单对象访问协议)http上执行远程方法的调用,也可以使用WSDLWeb服务描述语言)来完成完整的描述Web服务,然后用UDDI注册各个服务提供商提供的服务,以便共享他们。


常用的调用WebService的方法有哪些? 
1.
使用WSDL.exe命令行工具。 
2.
使用VS.NET中的Add Web Reference菜单选项

O/R Mapping 的原理 
答:利用反射,配置将对象和数据库表映射

广义上,ORM指的是面向对象的对象模型和关系型数据库的数据结构之间的相互转换。

狭义上,ORM可以被认为是,基于关系型数据库的数据存储,实现一个虚拟的面向对象的数据访问接口。

理想情况下,基于这样一个面向对象的接口,持久化一个OO对象应该不需要了解任何关系型数据库存储数据的实现细节。

什么叫应用程序域? 
答:应用程序域可以理解为一种轻量级进程。起到安全的作用。占用资源小。

程序与程序之间的隔离

out保留字怎么使用,什么时候使用 
答:有时为了从一个函数中返回多个值,我们需要使用out关键字,把输出值赋给通过引用传递给方法的变量(也就是参数)。但C#要求变量再被引用的前必须初始化。在调用该方法时,还需要添加out关键字 ref在方法外部初始化,out在方法内部初始化。

为什么不应该在.NET中使用out参数?它究竟好不好?

当想要一个函数产生多个输出时,可以使用out参数返回函数的输出值。这应该是out参数最大的作用。out参数的缺陷在于,它允许一个未初始化变量就在函数中作为out参数使用。这样并不能保证在访问一个作为out参数的变量时,它已经被初始化过,容易产生错误的结果。

tasklist /m "mscor*" 这句命令是干嘛的? 

列出所有使用了以" mscor"作为开头的dll或者exe的进程和模块信息 

列出调用指定的DLL模块的所有进程。如果没有指定模块名,显示每个进程加载的所有模块。

gacutil /l | find /i "Corillian" 这句命令的作用是什么?

全局程序集缓存中如果有Corillian就更新该程序集,没有就安装

sn -t foo.dll 这句命令是干嘛的?

显示程序集foo.dll的公钥标记

in-procout-of-proc的区别 .NET里的哪一项技术能够实现out-of-proc通讯? 

In-proc 发生在一个进程之内, Out-of-proc 发生在不同进程之间。

out-of-proc实现的技术:.NETremoting

Server.UrlEncodeHttpUtility.UrlDecode的区别 
Server.UrlEncode的编码方式是按照本地程序设置的编码方式进行编码的,HttpUtility.UrlEncode是默认的按照.netutf-8格式进行编码的。

DCOM需要防火墙打开哪些端口?端口135是干嘛用的?

135端口,因为DCOM的端口号是随机分配的,默认情况下,会分配1024以上的端口号,所以默认情况下,DCOM不能穿越防火墙。因为根本不晓得开哪个端口。但有解决办法可以使DCOM分配的端口号固定,135是远程过程调用(RPC)的默认端口


对比OOPSOA,它们的目的分别是什么?

我想OOPSOA应该没有对比性吧。OOP是一种编程模型,强调将复杂的逻辑分解出小的模块,特性是继承,封装和多态。而SOA是一个技术框架,技术框架和编程模型应该说不是一码事吧?SOA的思想是将业务逻辑封装成服务或者中间件提供给应用程序来调用,当然其组件化思想是继承和发扬了OOP的优点。

SOA:面向服务架构(Service Oriented Architecture,它属于一种组件架构模型,W3C的定义:一组公开发表接口,并且提供查询的组件,具有4个特性,每个服务具有明确的边界,服务是独立的,采用标准的契约定义和通信协议,服务是自解释的。如下图:


WCFWindows Commuication Foundation,Framework四个组件之一,是微软专门针对SOA应用程序提供的一个分布式变成框架,包括契约,服务运行时,消息,宿主和激活四个层次。(这里不详细介绍)  

Debug.WriteTrace.Write有什么不同?何时应该使用哪一个?

Debug.Write是调试的时候向跟踪窗口输出信息。当编译模式为debug的时候才有效,为release的时候Debug.Write在编译的时候会忽略,Trace则是在debugrelease两种模式下均可以向跟踪窗口输出信息。

Debug BuildRelease Build的区别,是否会有明显的速度变化?请说明理由。

Debug会产生pdb文件,release不会。Debug用于开发时的调试,不能要于部署,而release用于部署.debug编译一些特殊代码,比如#IFDEBUGDebug.Write等,而Release则会将那些特殊标记省略。

JIT是以assembly为单位发生还是以方法为单位发生?这对于工作区有何影响?

方法,道理很简单,因为对于一次运行,很可能只用到一个程序集中极少数类型和对象,而大部分可能并不会被使用,此时CLR傻乎乎的给整个程序集都给Compile了,CLR不是傻疯了么 JIT(即时编辑器)

什么叫JIT?什么是NGEN?它们分别有什么限制和好处

JustIn Time是指即时编译,它是在程序第一次运行时才进行编译,

NGEN是预先JIT,是指运行前事先就将生成程序集的加载和执行速度,因为它可以从本机镜像中还原数据代码和数据结构,而不是像JIT那样动态生成它们。

C#编写的程序,经过编译器把编译后,源代码被转换成Microsoft中间语言(MSIL)。MSIL不是真正可执行的代码。因此,要真正执行MSIL应用程序,还必须使用“JIT编译器”,对MSIL再次编译,以得到主机处理器可以真正执行本机指令。JIT编译器以即时方式编译MSMIL代码,以便应用程序执行。
.NET CLR中一代的垃圾收集器是如何管理对象的生命周期的?什么叫非确定性终结? Finalize()Dispose()之间的区别?
我只给你讲现在的原理,当开始进行垃圾回收工作时,clr会从最跟对象开始,如静态字段里的对象,遍历整个对象池,如果有引用就进行标记,剩下的对象都会被定为待回收对象,这时候这些对象已经没有任何引用可以访问到,他们被暂时搁置,等第二次垃圾回收的时候,才会把这些对象所占用的内存清理,当然之前还是会调用finalize方法的。因为会调用finalize方法,这时候有可能会把对象赋给一个静态字段或者其他对象可引用,那么这个对象就复活了,所以在没有被清理以前,对象的状态就叫非确定性终结。
Finalize自动释放资源,Dispose()用于手动释放资源
using() 语法有用吗?什么是IDisposable?它是如何实现确定性终结的。
有用,实现了IDisposiable的类在using中创建,using结束后会自定调用该对象的Dispose方法,释放资源。

a.Equals(b)a == b一样吗?

不一样。a.Equals(b)表示ab一致, a==b表示ab的值相等

在对象比较中,对象一致和对象相等分别是指什么?

对象一致是指两个对象是同一个对象,引用相同。而对象相等是指两个对象的值相同,但引用不一定相同

.NET中如何实现深拷贝(deepcopy)?

实现IClonable接口

深拷贝:指的是拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝。这样进行深拷贝后的拷贝对象就和源对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。举个例子,一个人叫张三,然后使用克隆技术以张三来克隆另外一个人叫李四,这样张三和李四就是相互独立的,不管张三缺胳膊还是李四少腿了都不会影响另外一个人。在.NET领域,值对象就是典型的例子,如int, Double以及结构体和枚举等。具体例子如下所示:

int source = 123;

// 值类型赋值内部执行深拷贝

int copy = source;

// 对拷贝对象进行赋值不会改变源对象的值

copy = 234;

// 同样对源对象赋值也不会改变拷贝对象的值

source = 345;

浅拷贝:指的是拷贝一个对象时,仅仅拷贝对象的引用进行拷贝,但是拷贝对象和源对象还是引用同一份实体。此时,其中一个对象的改变都会影响到另一个对象。例如,一个人一开始叫张三,后来改名字为张老三了,可是他们还是同一个人,不管张三缺胳膊还是张老三少腿,都反应在同一个人身上。在.NET中引用类型就是一个例子。如类类型。具体例子如下所示:

public class Person

    {

       public string Name { get; set; }

    }

 

    classProgram

    {

       static void Main(string[] args)

        {

           Person sourceP = new Person() { Name = "张三" };

           Person copyP = sourceP; // 浅拷贝

           copyP.Name = "张老三"; // 拷贝对象改变Name

           // 结果都是"张老三",因为实现的是浅拷贝,一个对象的改变都会影响到另一个对象

           Console.WriteLine("Person.Name: [SourceP: {0}] [CopyP:{1}]",sourceP.Name, copyP.Name);

           Console.Read();

        }

    }

请解释一下IClonable

IClonable方法是实现深度复制的接口,实现它应该能深度复制一个对象出来。深度复制的特征的调用对象的构造方法,创建新的对象,包括创建对象中嵌套的引用对象的新实例。而Shadow复制则不同,是浅表复制,不重新创建新实例。浅表复制的实现是Object.MemberWiseClone().

深度复制(Deep Copy)与浅表复制(Shadow Copy)的比较 
  public class Name 
    { 
        public string FirstName; 
        public string LastName; 
    } 
    public class Person:ICloneable 
    { 
        public Name PersonName; 
        public string Email; 
        /** <summary> 
        /// Deep Copy
的例子 
        /// </summary> 
        ///<returns></returns> 
        public Object Clone() 
        { 
            Person p =new Person(); 
            p.Email =this.Email; 
            p.PersonName= new Name(); 
           p.PersonName.FirstName = this.PersonName.FirstName; 
           p.PersonName.LastName = this.PersonName.LastName; 
            returnp; 
        }

        publicvoid ChangLastName(string lastName) 
        { 
           this.PersonName.LastName = lastName; 
        } 
        public static void Main() 
        { 
            Person p =new Person(); 
            p.PersonName= new Name(); 
           p.PersonName.LastName = "jill"; 
           p.PersonName.FirstName = "zhang"; 
            p.Email ="jillzhang@126.com"; 
            PersonsameNamePerson = p.Clone() as Person; 
           sameNamePerson.ChangLastName("clr_"); 
           Console.WriteLine(p.PersonName.LastName); 
           Console.WriteLine(sameNamePerson.PersonName.LastName);           

           Person samePerson = p.MemberwiseClone() as Person; 
            samePerson.ChangLastName("Shadow");

           Console.WriteLine(p.PersonName.LastName); 
           Console.WriteLine(sameNamePerson.PersonName.LastName); 
           Console.Read(); 
        } 
    }

特性能够放到某个方法的参数上?如果可以,这有什么用?

可以,作用可以对参数有进一步限定,比如输入参数为int类型,可以通过允许AttributeTargets=ParameterInfoAttribute自定义实现来限定输入参数的大小,比如当输入参数小于100的时候便抱错。

如何给服务器端控件增加客户端脚本。 
答:控件的Attributes

私有程序集与共享程序集有什么区别? 
一个私有程序集通常为单个应用程序所使用,并且存储于这个应用程序所在的目录之中,或此目录下面的一个子目录中。共享程序集通常存储在全局程序集缓存(Global Assembly Cache)之中,这是一个由.NET运行时所维护的程序集仓库。共享程序集通常是对许多应用程序都有用的代码库,比如.NET Framework类。

请解释ASP.NET中以什么方式进行数据验证 
Aps.net 中有非空验证,比较验证,取值范围验证,正则表达式验证及客户自定义验证五大控件,另还有一个集中验证信息处理控件

WEB控件可以激发服务端事件,请谈谈服务端事件是怎么发生并解释其原理?自动传回是什么?为什么要使用自动传回。 
web控件发生事件时,客户端采用提交的形式将数据交回服务端,服务端先调用Page_Load事件,然后根据传回的状态信息自动调用服务端事件

自动传回是当我们在点击客户端控件时,采用提交表单的形式将数据直接传回到服务端,只有通过自动传回才能实现服务端事件的机制,如果没有自动回传机制就只能调用客户端事件,而不能调用服务端事件。

请解释web.config文件中的重要节点 
appSettings包含自定义应用程序设置。 
system.web
系统配置 
compilation
动态调试编译设置 
customErrors
自定义错误信息设置 
authentication
身份验证,此节设置应用程序的身份验证策略。 
authorization
授权, 此节设置应用程序的授权策略。

 请解释ASPNET中的web页面与其隐藏类之间的关系? 
一个ASP.NET页面一般都对应一个隐藏类,一般都在ASP.NET页面的声明中指定了隐藏类例如一个页面Tst1.aspx的页面声明如下 <%@ Page language="c#"Codebehind="Tst1.aspx.cs" AutoEventWireup="false"Inherits="T1.Tst1" %> Codebehind="Tst1.aspx.cs" 表明经编译此页面时使用哪一个代码文件 Inherits="T1.Tst1" 表用运行时使用哪一个隐藏类

 

写一个标准的lock(),在访问变量的前后创建临界区,要有"双重检查

//这即是双检锁(double-check locking): 例子如下

   publicsealedclassSingleton

   {

       privatestaticObject s_lock = newObject();

       privatestaticSingleton s_value;

       //私有构造器组织这个类之外的任何代码创建实例

       private Singleton(){ }

       // 下述共有,静态属性返回单实例对象

       publicstaticSingleton Value

       {

            get

            {

                // 检查是否已被创建

                if (s_value == null)

                {

                    // 如果没有,则创建

                    lock (s_lock)

                    {

                        // 检查有没有另一个进程创建了它

                        if (s_value == null)

                        {

                            // 现在可以创建对象了

                            s_value = newSingleton();

                        }

                    }

                }

                return s_value;

            }

       }

}

 

什么叫FullTrust?放入GACassembly是否是FullTrust?

fulltrust 是一款对表单模板安全级别完全信任的应用软件。


什么是viewstate,能否禁用?是否所用控件都可以禁用
Viewstate是保存状态的一种机制,EnableViewState属性设置为false即可禁用

当发现不能读取页面上的输入的数据时很有可能是什么原因造成的?怎么解决? 
很有可能是在Page_Load中数据处理时没有进行PageIsPostBack属性判断

ASP.NET 上下文对象

ASP.NET 提供了一系列对象用来给当前请求,将要返回到客户端的响应,以及 Web 应用本身提供上下文信息。

上下文对象是指HttpContext类的Current 属性,当我们在一个普通类中要访问内置对象(Response,Request,Session,Server,Appliction)时就要以使用此对象。

delegate是引用类型还是值类型?enumint[]string呢(难度系数40%)?

答案:enum值类型,delegate引用类型,int[]引用类型,string引用类型。

base这个关键字有哪几种语法?override呢?

答案:base两个语法,分别是调用基类构造函数和调用基类方法,override用于重写基类方法。

i++++i的区别

如果有表达式 a =i++  它等价于 a = i ; i = i + 1;

如果有表达式 a =++i  它等价于   i = i + 1; a = i;

两者的区别是:前者是先赋值,然后再自增;后者是先自增,后赋值。

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

属性是一种成员,它提供灵活的机制来读取、写入或计算私有字段的值。 属性可用作公共数据成员,但它们实际上是称为访问器的特殊方法。 这使得可以轻松访问数据,还有助于提高方法的安全性和灵活性。

public class Person

{

    private string _name ="No one";

    public Person(stringname)

    {

       _name = name;

    }

    public string Name

    {

       get { return _name;}

    }

};

用法:

    Person p = new Person("Arya");

System.Console.WriteLine(p.Name);

索引器允许类或结构的实例就像数组一样进行索引。 索引器类似于属性,不同之处在于它们的取值函数采用参数。   

    public string this[stringkey]

    {

       get { if (key =="Name")

              return _name;

           return null;}

       set { if (key =="Name")

              _name = value;

           return;}

    }

跟属性一样,索引器可以只有set访问器或者get访问器,或者二者都有,不过,索引器可输入参数。用法:

 p ["Name"] = "Anonymous";

System.Console.WriteLine(p ["Name"]);

索引器和数组比较:

(1)索引器的索引值(Index)类型不受限制

(2)索引器允许重载

(3)索引器不是一个变量

索引器和属性的不同点

(1)属性以名称来标识,索引器以函数形式标识

(2)索引器可以被重载,属性不可以

(3)索引器不能声明为static,属性可以

索引器(Indexer)C#引入的一个新型的类成员,它使得对象可以像数组那样被方便,直观的引用。索引器非常类似于我们前面讲到的属性,但索引器可以有参数列表,且只能作用在实例对象上,而不能在类上直接作用。

描述线程与进程的区别?

线程(Thread)与进程(Process)二者都定义了某种边界,不同的是进程定义的是应用程序与应用程序之间的边界,不同的进程之间不能共享代码和数据空间,而线程定义的是代码执行堆栈和执行上下文的边界。一个进程可以包括若干个线程,同时创建多个线程来完成某项任务,便是多线程。而同一进程中的不同线程共享代码和数据空间。用一个比喻来说,如果一个家庭代表一个进程,在家庭内部,各个成员就是线程,家庭中的每个成员都有义务对家庭的财富进行积累,同时也有权利对家庭财富进行消费,当面对一个任务的时候,家庭也可以派出几个成员来协同完成,而家庭之外的人则没有办法直接消费不属于自己家庭的财产。

如何实现连接池

确保你每一次的连接使用相同的连接字符串(和连接池相同);只有连接字符串相同时连接池才会工作。如果连接字符串不相同,应用程序就不会使用连接池而是创建一个新的连接。

优点

使用连接池的最主要的优点是性能。创建一个新的数据库连接所耗费的时间主要取决于网络的速度以及应用程序和数据库服务器的(网络)距离,而且这个过程通常是一个很耗时的过程。而采用数据库连接池后,数据库连接请求可以直接通过连接池满足而不需要为该请求重新连接、认证到数据库服务器,这样就节省了时间。

缺点

数据库连接池中可能存在着多个没有被使用的连接一直连接着数据库(这意味着资源的浪费)。

技巧和提示

1当你需要数据库连接时才去创建连接池,而不是提前建立。一旦你使用完连接立即关闭它,不要等到垃圾收集器来处理它。

2在关闭数据库连接前确保关闭了所有用户定义的事务。

3不要关闭数据库中所有的连接,至少保证连接池中有一个连接可用。如果内存和其他资源是你必须首先考虑的问题,可以关闭所有的连接,然后在下一个请求到来时创建连接池。

常见的几种常见的数据库连接池

Dbcp dbcp apache 上的一个Java连接池项目。特点是包含基本功能且配置简单,没有连接池监控功能,稳定性尚可,据说大并发下面速度稍慢。

cp30特点是包含基本功能且配置简单,没有连接池监控功能,稳定性尚可,据说大并发下面稳定性有一定保证。

Druid(阿里的开元项目)

据网上测试对比,比目前的DBCPC3P0数据库连接池性能更好。且提供连接池监控功能。

连接池FAQ

1 何时创建连接池?

当第一个连接请求到来时创建连接池;连接池的建立由数据库连接的连接字符创来决定。每一个连接池都与一个不同的连接字符串相关。当一个新的连接请求到来时如果连接字符串和连接池使用的字符串相同,就从连接池取出一个连接;如果不相同,就新建一个连接池。

2 何时关闭连接池?

当连接池中的所有连接都已经关闭时关闭连接池。

3 当连接池中的连接都已经用完,而有新的连接请求到来时会发生什么?

当连接池已经达到它的最大连接数目时,有新的连接请求到来时,新的连接请求将放置到连接队列中。当有连接释放给连接池时,连接池将新释放的连接分配给在队列中排队的连接请求。你可以调用closedispose将连接归还给连接池。

4 我应该如何允许连接池?

对于.NET应用程序而言,默认为允许连接池。(这意味着你可以不必为这件事情做任何的事情)当然,如果你可以在SQLConnection对象的连接字符串中加进Pooling=true;确保你的应用程序允许连接池的使用。

5 我应该如何禁止连接池?

ADO.NET默认为允许数据库连接池,如果你希望禁止连接池,可以使用如下的方式:

1) 使用SQLConnection对象时,往连接字符串加入如下内容:Pooling=False;

2) 使用OLEDBConnection对象时,往连接字符串加入如下内容:OLE DBServices=-4;


ADO.net中常用的对象有哪些?分别描述一下。 
答: 
Connection   打开数据库连接 
Command     执行数据库命令 
DataAdapter  连接数据,执行数据库命令,填充DataSet 
DataSet    数据在内存中的缓存,数据结构 
DataReader   只读向前的读取数据库

DataReader和DataSet的异同 
DataReader
使用时始终占用SqlConnection,在线操作数据库..任何对SqlConnection的操作都会引发DataReader的异常..因为DataReader每次只在内存中加载一条数据,所以占用的内存是很小的..因为DataReader的特殊性和高性能.所以DataReader是只进的..你读了第一条后就不能再去读取第一条了.. 
DataSet则是将数据一次性加载在内存中.抛弃数据库连接..读取完毕即放弃数据库连接..因为DataSet将数据全部加载在内存中.所以比较消耗内存...但是确比DataReader要灵活..可以动态的添加行,列,数据.对数据库进行回传更新操作...









  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值