C#的基础知识问答
1. 简述 private、 protected、 public、 internal、internal protected 修饰符的访问权限。
答:
没有标明访问级别的就是private。
private:私有成员,只能在类的内部才可以访问。
protected: 保护成员,只能从所在类和所在类派生的子类进行访问
public: 公共成员,不限制对该类的访问。
internal: 访问仅限于当前程序集(Assembly)。
internal protected: 访问仅限于程序集(Assembly)和程序集(Assembly)所属的类派生的类型。
2.什么是命名空间?它的作用是什么?
答:命名空间是一组保持唯一的名称。命名空间用于声明一个范围,此命名空间范围允许您组织代码并提供了创建全局唯一类型的方法。命名空间是避免命名冲突的一种方式。(MSDN)
3.什么是程序集(Assembly)?它有什么特性。
答:程序集(Assembly)是自我描述的安装单元,由一个或多个文件组成,一个程序集可以是一个包括元数据的DLL或EXE,也可以由多个文件组成。(C#高级编程)
程序集是可重用、无版本冲突并且可自我描述的公共语言运行库应用程序构造块。程序集提供使运行库能够充分了解应用程序的内容并强制使用应用程序定义的版本控制和依赖项规则的结构。这些概念对解决版本控制问题和简化运行库应用程序的部署至关重要。(MSDN)
特性:
(1) 程序集是自我描述的.程序集包含描述程序集的元数据,元数据包括从程序集导出的类和一个清单。
(2) 版本的互相依赖性在程序集的清单中进行了记录。引用程序集的版本被存储在程序集的清单中。
(3) 程序集可以进行并行加载 .在。NET中同一个程序集的不同版本可以在同一个进程中使用。
(4) 应用程序使用应用程序域(Application Domain)来确保其独立性.使用应用域,许多应用程序可以独立的运行在一个进程中。
(5) 安装非简单,只要复制一个程序集中的所有文件。
4.什么是应用程序域AppDomain?
答1:在.NET结构中,应用程序域是应用程序的一个边界,运行时就不能访问同一个进程中另外一个应用程序的内存,多个应用程序可以运行在多个应用域的一个进程中。(C#高级编程)
答2:应用程序域(通常为 AppDomain)是一个用于隔离应用程序的虚拟进程。在同一应用程序范围内(换句话说,以应用程序入口点开头的对象激活序列中的任何位置)创建的所有对象都创建在同一应用程序域中。多个应用程序域可以存在于单个操作系统进程中,这使它们成为应用程序隔离的轻量方法。
操作系统进程通过提供一个独特的内存地址空间来提供隔离。虽然这很有效,但成本很高,而且不能扩展到大型 Web 服务器所需的数目。另一方面,公共语言运行库通过管理运行在应用程序域中的代码的内存使用来强制应用程序隔离。这可确保它不会访问域边界以外的内存。注意只有类型安全代码才能以这种方式进行管理(运行库在不安全代码加载到应用程序域中时无法保证隔离)很重要。(http://www.w3sky.com/2469.html)
答3:应用程序域 (AppDomain) 可以被看作一个轻型的进程。在一个 Win32 进程中可以存在多个ppDomain。AppDomain 的主要目的是将应用程序和其它应用程序隔离开来。
通过使用独立的地址空间,Win32 进程提供隔离性。这种方法很有效,但开销很大并且伸缩性不好。.NET 运行库通过控制对内存的是用来施加 AppDomain 隔离—AppDomain 中的所有内存是由 .NET 运行库来管理的,所以运行库可以确保 AppDomain 之间不能访问彼此的内存。(http://blog.csdn.net/ianc/)
单个进程可以运行多个应用程序域,并具有在单独进程中所存在的隔离级别。在单个进程中运行多个应用程序提高了服务器伸缩性。
4. 一列数的规则如下: 1、1、2、3、5、8、13、21、34...... 求第30位数是多少, 用递归算法实现。
答1:
class Recursion
{
static void Main(string[] args)
{
int result;
Recursion rc = new Recursion();
result = rc.RecursionCal(30);
Console.Write("The result:{0}",result);
}
private int RecursionCal(int i)
{
int result;
if( i <= 2)
{
result = 1;
}
else
{
result = RecursionCal(i - 2) + RecursionCal(i - 1);
}
return result;
}
}
答2:
public class MainClass
{
public static void Main()
{
Console.WriteLine(Foo(30));
}
public static int Foo(int i)
{
if (i <= 0)
return 0;
else if(i > 0 && i <= 2)
return 1;
else return Foo(i -1) + Foo(i - 2);
}
}
5 什么时候使用static?它的优点是什么?使用static应该注意什么?
当成员函数和成员变量不依赖具体对象,为整个类而非某个对象提供服务时,就使用static。使用static 修饰符声明属于类型本身而不是属于特定对象的静态成员。
优势:
(1)节省内存
(2)static的方法就是类级别的,不需实例化出一个对象,即可使用该方法。
注意事项:
(1)类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致
了它仅能访问类的静态数据和静态成员函数。
(2)静态数据成员是静态存储的,所以必须对它进行初始化。(C#会自动初始化,C++则需要人工初始化)
(3)访问静态成员函数或静态成员变量只能通过类来访问,不能通过对象来访问。
6 C#中的委托是什么?事件是不是一种委托?
委托在C#中可以看作是对象的一种新类型。委托把一个方法作为参数传入到另一个方法,通过传递地址的机制完成。它相当于C/C++的函数指针。
事件是一种特殊的委托。委托支持事件处理,可以将事件处理过程注册给委托。触发事件就调用委托。
7 重载(Overload)、覆盖(Override)、隐藏(Hide)几个的区别
重载(overload):在一个类中定义方法的不同版本,重载方法的名称相同但是重载的签名不同--即参数名称、参数个数或参数类型不同。只有返回类型相同是不行的。
覆盖(Override):将基类的方法声明为virtual,在派生类中提供另外一个带有相同签名的方法并在声明中含有override关键字,则重写了基类的方法。
隐藏(Hide):在基类中方法没有声明为virtual,则在派生类中提供另外一个带有相同签名的方法并在声明中含有new关键字,则新方法隐藏基类的方法。
方法的签名:描述如何调用该方法所需要的一组信息:方法的名称、返回类型,参数个数,参数类型。
8 请编程实现一个冒泡排序算法?
答1:C语言
#include<stdio.h>
main()
{
int num[10];
int i, j, temp;
printf("Please input 10 numbers:\n");
for(i = 0 ; i < 10; i++)
scanf("%d", &num[i]);
for(i = 1; i < 10; i++)
for(j = 0; j < 10 - i; j++)
{
if(num[j] > num[j+1])
{
temp = num[j] ;
num[j] = num[j+1];
num[j+1] = temp;
}
}
printf("The sorted numbers:\n");
for(i = 0 ; i < 10; i++)
printf("%d\n",num[i]);
}
答2:C#语言
static void Main(string[] args)
{
int[] num = new int[10];
Console.WriteLine("Please input 10 numbers:");
for(int i = 0; i < 10; i++)
num[i] = Int32.Parse(Console.ReadLine());
for(int i = 1; i < 10; i++)
for(int j = 0; j < 10 - i; j++)
{
if(num[j] > num[j+1])
{
num[j] = num[j] + num[j+1];
num[j+1] = num[j] - num[j+1];
num[j] = num[j] - num[j+1];
}
}
Console.WriteLine("The sorted numbers:");
for(int i = 0; i < 10; i++)
Console.WriteLine("{0}", num[i]);
}
9 请编程实现一个选择排序算法?
答1:C语言
#include<stdio.h>
main()
{
int num[10];
int i, j, temp;
printf("Please input 10 numbers:\n");
for(i = 0 ; i < 10; i++)
scanf("%d", &num[i]);
for(i = 0; i < 9; i++)
for(j = i+1; j < 10; j++)
{
if(num[j] < num[i])
{
temp = num[i] ;
num[i] = num[j];
num[j] = temp;
}
}
printf("The sorted numbers:\n");
for(i = 0 ; i < 10; i++)
printf("%d\n",num[i]);
}
答2:C#语言
static void Main(string[] args)
{
int[] num = new int[10];
Console.WriteLine("Please input 10 numbers:");
for(int i = 0; i < 10; i++)
num[i] = Int32.Parse(Console.ReadLine());
for(int i = 0; i < 9; i++)
for(int j = i + 1; j < 10; j++)
{
if(num[j] < num[i])
{
num[i] = num[i] + num[j];
num[j] = num[i] - num[j];
num[i] = num[i] - num[j];
}
}
Console.WriteLine("The sorted numbers:");
for(int i = 0; i < 10; i++)
Console.WriteLine("{0}", num[i]);
}
10 描述一下C#中索引符的实现过程,是否只能根据数字进行索引?
答:C#通过提供索引器,可以象处理数组一样处理对象。
定义索引符的方式与定义属性的方式一样,使用get和set函数,索引符的名称是关键字this.
实现过程:
(1)在类里面定义索引符
public string this [int index]
{
get {
if(index < 0 || index > length)
{
throw new IndexOutOfRangeException();
}
return "Value" + index; }
set{
if(index < 0 || index > length)
{
throw new IndexOutOfRangeException();
}
myObjectValue = value;
}
}
(2)使用类创建对象后,就可以使用索引符
NewClass newClass = new NewClass();
然后就象使用数组一样使用newClass[i]。
11 索引和属性的异同?
相同点:
(1)获取对象或是类的静态成员的值。
(2)定义类似,使用get和set函数。
不同点:
(1)类的每一个属性都必须拥有唯一的名称,而类里定义的每一个索引器都必须拥有唯一的签名(signature)或者参数列表(这样就可以实现索引器重载)。
(2)属性可以是static(静态的)而索引器则必须是实例成员。
(3)为索引器定义的访问,函数可以访问传递给索引器的参数,而属性访问函数则没有参数。
12 求以下表达式的值,写出您想到的一种或几种实现方法: 1-2+3-4+……+m
答:我写出了三种:
答1:
private static int CalResult(int m)
{
if(m % 2 == 0)
{
return -(m / 2);
}
else
{
return m - (int)(m / 2);
}
}
答2:
private static int CalResult(int m)
{
int result = 0;
for(int i = 1; i < m + 1; i++)
{
if(i % 2 == 0)
{
result = result - i;
}
else
{
result = result + i;
}
}
return result;
}
答3:
private static int CalResult(int m)
{
int result = 0;
for(int i = 1; i < m + 1; i++)
{
result = (i % 2 == 0) ? result - i : result + i;
}
return result;
}
13 CTS、CLS、CLR分别作何解释?
答1:CTS:公共类型系统。为了实现语言的互操作性,必须有一组各种语言都认可的基本数据类型,这样才能对所有语言进行标准化处理。CTS就提供了这个功能,还提供了定义定制类的规则。
CLS:公共语言规范。这是确保代码可以在任何语言中访问的最小标准集体。所有用.NET的编译器都应支持CLS。CLS构成了可以在.NET和IL中使用的功能子集,代码也可以使用CLS外部的功能。如果非CLS功能在代码所在装配件的外部是可见的,那么这些功能就不能在某些语言中使用。
CLR:公共语言运行库。它可以处理加载程序、运行程序的代码,以及提供所有支持服务的代码。
14 什么是受管制的代码和未受管制的代码?
答:受管制的代码:在.NET环境中运行的任何代码成为受管制的代码(managed code),.NET环境外部的其他代码也运行在Windows上,这些代码称为未受管制的代码(unmanaged code)。
15 解释一下中间语言(IL)。
答:在.NET运行时加载和运行代码时候,这种语言确定代码的位置,在编译受管制的代码时,编译器实际上使用中间语言,CLR处理执行前的最后编译阶段,IL可以非常快速的编译为内部的机器代码,同时支持.NET功能。
16 解释以下JIT编译。
答:Just-in-Time(JIT)编译,表示执行编译过程的最后阶段,即从中间语言转换为内部机器代码。部分代码是按需要即时编译的。
17 在下面的例子里
class A
{
public A()
{
PrintFields();
}
public virtual void PrintFields(){}
}
class B:A
{
int x=1;
int y;
public B()
{
y=-1;
}
public override void PrintFields()
{
Console.WriteLine("x={0},y={1}",x,y);
}
}
当使用new B()创建B的实例时,产生什么输出?
答:答:x=1,y=0
18 什么是装箱和拆箱?
答:装箱是从值类型转换到引用类型。拆箱是从引用类型转换到值类型。
19 什么是强类型系统?
答:每个变量有类型,每个表达式有类型,而且每种类型是严格定义的。其次,所有的数值传递,不管是直接的还是通过方法调用经由参数传过去的都要先进行类型相容性的检查。编译器对所有的表达式和参数都要进行类型相容性的检查以保证类型是兼容的。任何类型的不匹配都是错误的,在编译器完成编译以前,错误必须被改正。
20 .NET中读写数据库需要用到那些类?他们的作用?
答:
数据库共享的类:
DataSet--它创建的对象包含一组DataTables,以及这些表之间的关系。
DataTable--它创建的对象作为数据的一个容器,DataTable由一个或多个DataColumn组成,每个DataColumn
由一个或多个DataRow生成。
DataRow-- 类似与数据库表的一行。
DataColumn-- 包含列的含义。例如名称和数据类型。
Constraint--为DataColumn(或一组数据列)定义规则,例如惟一值。
DataColumnMapping--用DataTable中的列名映射数据中的列名。
DataTableMapping--将数据库中的表名映射到DataSet中的DataTable中。
数据库特定的类:
Sqlcommand、Oledbcommand --SQL语句的包装或存储过程的调用。
SqlcommandBuilder、OledbcommandBuilder --用于从一个select子句中生成SQL语句(例如插入、更新、删
除)的类。
Sqlconnection、 Oledbconnection--数据库连接
SqlDataAdapter、OledbDataAdapter--用语存储选择插入、更新、删除语句的类,因此可以用于生产
DataSet和更新Database.
SqlDataReader、OledbDataReader--只向前的连接数据读取器。DataSet和更新Database.
SqlParameter、OledbParameter--为存储过程定义一个参数。
SqlTransaction、OledbTransaction--一个数据库处理事物,包装在一个对象中。