C#笔试知识点
1.String与Stringbuilder的区别:
String自动分配内存大小,每次往里面写新东西,就会重新分配一段内存,然后把地址指向新的这块空间,是由C#内存管理自动管理的。
Stringbuilder会事先分配好一段空间,append的时候,是操作的同一块空间,如果新串超过原本大小,内存空间自动加倍。
2.C#如何调用c++静态库(lib):
可以用CLR(新)或者Managed c++(老)将lib封装成managed dll供C#直接调用。
将lib封装成native dll,C#中通过DllImport调用dll。
将lib封装成native dll, 再用CLR封装native dll成managed dll供C#直接调用。
将lib封装为COM,在C#中调用COM。
3.长度为100的字符串,从abcdefgh中随机抽取100个字符组成:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace csharpconsole
{
class Program
{
static void Main(string[] args)
{
StringBuilder strOut = new StringBuilder(0, 100);
String strSrc = "a, b, c, d, e, f, g, h";
String[] strDes = strSrc.Split(‘,‘);
int len = strDes.Length;
Random rd = new Random();
int i = 0;
for (; i < 100; i++)
{
StrOut = strDes[rd.Next(len)];
}
Console.ReadKey();
}
}
}
4.产生一个int数组,长度为100,并向其中随机插入1-100,并且不能重复:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
namespace csharpconsole
{
class Program
{
static void Main(string[] args)
{
int[] intArr = new int[100];
ArrayList myList = new ArrayList();
Random rd = new Random();
int num = 0; /* 1-100产生随机数 */
int i = 0;
/* 生成100个不重复的1-100的数 */
while (myList.Count < 100)
{
num = rd.Next(1, 101);
if (!myList.Contains(num))
{
myList.Add(num);
}
}
for (; i < 100; i++)
{
intArr[i] = (int)myList[i];
}
foreach (int a in intArr)
{
Console.WriteLine(a);
}
Console.ReadKey();
}
}
}
5.把一个Array复制到ArrayList里:
foreach (object o in array)
{
arrayList.Add(o);
}
6.写一个函数计算当参数为N的值:1-2+3-4+5-6+7……+N:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace csharpconsole
{
class Program
{
static void Main(string[] args)
{
int num = 0;
String str = "";
int sum = 0;
Console.WriteLine("请输入一个100以内的数:");
str = Console.ReadLine();
num = int.Parse(str);
for (int i = 1; i <= num; i++)
{
sum += ((int)Math.Pow(-1, i + 1)) * i;
}
Console.WriteLine(sum);
Console.ReadKey();
}
}
}
7.斐波拉契数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34……:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace csharpconsole
{
class Program
{
static int MyALG(int num)
{
if (num <= 0)
{
return 0;
}
else if ((1 == num) || (2 == num))
{
return 1;
}
else
{
return (MyALG(num - 1) + MyALG(num - 2));
}
}
static void Main(string[] args)
{
int result = 0;
result = MyALG(30);
Console.WriteLine(result.ToString());
Console.ReadKey();
}
}
}
8.理解接口的作用:
9.虚方法(virtual)和抽象类(abstract)的区别和联系:
(1)、抽象方法只有声明没有实现代码,需要在子类中实现;虚拟方法有声明和实现代码,并且可以在子类中重写,也可以不重写使用父类的默认实现。
(2)、抽象类不能被实例化(不可以new),只能实例化实现了全部抽象方法的派生类;而包含虚方法的类可以实例化。
(3)、虚方法与多态性关系密切,虚方法允许派生类完全或部分重写该类的方法,需写方法体。抽象方法只是一个定义,没有方法体,也就是没有{},也不要在里面写内容。
(4)、抽象方法是虚拟方法两个相像的一点是都用override重写。
10.值类型和引用类型的区别:
值类型:存储在堆栈中。bool, byte, chat, decimal, double, enum, float, int, long, sbyte, short, strut, uint, ulong, ushort。
引用类型:存储在托管堆中。class, delegate, interface, object, string。引用类型包含一个指针,指向堆中存储对象本身的位置。因为引用类型只包含引用,不包含实际的值,对方法体内参数所做的任何修改都将影响传递给方法调用的引用类型的变量。
托管代码:基于CLR的语言编译器开发的代码称为托管代码。
托管堆:是CLR中自动内存管理的基础。初始化新进程时,运行时会为进程保留一个连续的地址空间区域。这个保留的地址空间被称为托管堆。托管堆维护着一个指针,用它指向将在堆中分配的下一个对象的地址。最初,该指针设置为指向托管堆的基址。
由于对对象的引用会在程序运行过程中被传递,所以引用类型不能够被用户方便的释放,.NET的垃圾收集处理机制,会在引用为0的时候,自动释放掉引用类型变量。
11.结构和类的区别:
结构是值类型,类是引用类型;结构不能有构造函数和析构函数;类可以同时有构造函数和析构函数;结构不支持继承,而类支持继承。
12.委托(delegate)和事件(event):
委托:委托是类型安全,面向对象的函数指针的实现,并且在许多一个组件需要回调到使用它的组件这样的情况下使用。
事件:事件是使一个类或对象能够提供通知的成员。事件声明像域声明一样,除了声明包含event关键字并且类型必须为委托类型。
13.对象类型(object)和动态类型(dynamic)的区别:
对象类型:是所有数据类型的终极基类。Object是System.Object类的别名。当一个值类型转换为对象类型时,则被称为装箱;另一方面,当一个对象类型转换为值类型时,则被称为 拆箱。
动态类型:可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。
动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。
14.传入参数:
包括值参数,引用参数,输出参数和参数数组。
引用参数(ref关键字):实参在调用前需要初始化,并且值可以被传入和传出;
输出参数(out关键字):实参在调用前不必初始化,在方法中必须要初始化,值只能传出不能传入。
15.C#与C的区别:
C#不支持#include语句。它只用using语句。
C#中,类定义在最后不使用分号。
C#不支持多重继承。
数据类型的显示转换在C#中比C中安全很多。
C#中switch也可用于字符串值。
命令行参数数组(params,即可变参数)的行为在C#中和C中不一样。
16.接口和类的区别:
(1)、接口类似于类,但接口的成员都没有执行方式,它只是方法、属性、事件和索引符的组合而已,并且也只能包含这四种成员;类除了这四种成员之外还可以别的成员(如字段)。
(2)、不能实例化一个接口,接口只包括成员的签名;而类可以实例化(abstract类除外)。
(3)、接口没有构造函数,类有构造函数。
(4)、接口不能进行运算符的重载,类可以进行运算符重载。
(5)、接口的成员没有任何修饰符,其成员总是公共的,而类的成员则可以有修饰符(如:虚拟或者静态)。
(6)、派生于接口的类必须实现接口中所有成员的执行方式,而从类派生则不然。
17.const和readonly区别:
readonly修饰的字段,其初始化仅是固定了其引用(地址不能修改),但它引用的对象的属性是可以更改的。
const声明的字段不能使用static修饰符,而readonly可以使用static修饰符。
const字段只能在声明时初始化,而readonly可以在声明时或在构造函数中初始化。
const字段的值在在编译阶段就计算出来了,而readonly的值在运行时计算。
18.异常(try、catch、finally、throw):
try:可能抛出异常的代码;
catch:出现异常后针对的处理办法;
finally:不论是否出现异常都执行的代码(通常是一些释放资源的代码),finally块是可以省略的。
一个try块可以对应多个catch块,其中按抛出的异常类寻找对应的catch块,也可能找不到,如果一直没有找到就会终止程序,返回一条错误代码。
异常处理可以互相嵌套,在内层找不到对应的处理代码,会向外层找。
(1)、try、catch、finally例子:
/*
结果:
finally1
试图除以0.
finally2
*/
static void Main(string[] args)
{
int n1 = 10;
int n2 = 0;
int result;
/* 由于除数为0,抛出异常,内层找不到会找外层的异常catch处理 */
try
{
/* try catch的嵌套 */
try
{
result = n1 / n2;
Console.WriteLine("{0}", result);
}
catch (MemberAccessException e)
{
Console.WriteLine(e.Message);
}
finally
{
Console.WriteLine("finally1");
}
}
/* 2个外层catch */
catch (DivideByZeroException e)
{
/* 最后找到是由此catch处理 */
Console.WriteLine(e.Message);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
Console.WriteLine("finally2");
}
Console.ReadKey();
}
(2)、自定义异常throw例子:
/*
结果:
除数为0
*/
/* 自定义异常必须继承于ApplicationException类 */
private class TestException : ApplicationException
{
public string msg;
public TestException(string msg)
{
this.msg = msg;
}
}
/* 定义方法和异常抛出代码 */
static double mul(double n1, double n2)
{
if (n2 == 0.0)
{
/* 除数为0就抛出异常 */
throw new TestException("除数为0");
}
return n1 / n2;
}
static void Main(string[] args)
{
double n1 = 10.0;
double n2 = 0.0;
double result;
try
{
/* double除法默认除数为0会返回一个很大的值 */
result = mul(n1, n2);
Console.WriteLine(result);
}
catch (TestException e)
{
Console.WriteLine(e.msg);
}
Console.ReadKey();
}
19.特性(Attribute)和反射(Reflection):
特性为类、方法、结构、枚举、组件等添加自定义的属性(元数据),反射可以获取这些属性,需要的时候通过反射提取出来使用。
20.属性(Property)和索引器(Indexer):
属性要通过一个叫做访问器(accessor)的东东,对私有成员变量进行安全带验证的访问方式。即get、set属性。
索引器是可以通过方便的方式访问对象(实例化的类)中的成员数组或集合。(注意这里是数组或集合)
21.线程:
System.Threading名字空间。
Start():启动线程;
Sleep(int):静态方法,暂停当前线程指定的毫秒数;
Abort():通常使用该方法来终止一个线程;
Suspend():该方法并不终止未完成的线程,它仅仅挂起线程,以后还可恢复;
Resume():恢复被Suspend()方法挂起的线程的执行。
(1)、线程使用:
namespace Test
{
class Program
{
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(TestMethod));
Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod));
t1.IsBackground = true; //设置当前子线程为后台线程,为后台线程意味着,主线程关闭后,其他子线程都同时关闭
t2.IsBackground = true;
t1.Start();
t2.Start("hello");
Console.ReadKey();
}
public static void TestMethod()
{
Console.WriteLine("不带参数的线程函数");
}
public static void TestMethod(object data)
{
string datastr = data as string;
Console.WriteLine("带参数的线程函数,参数为:{0}", datastr);
}
}
}
(2)、线程池:
namespace Test
{
class Program
{
static void Main(string[] args)
{
//将工作项加入到线程池队列中,这里可以传递一个线程参数
ThreadPool.QueueUserWorkItem(TestMethod, "Hello");
Console.ReadKey();
}
public static void TestMethod(object data)
{
string datastr = data as string;
Console.WriteLine(datastr);
}
}
}
(3)、Lock互斥锁:
就是多线程访问临界区代码,一次只能让一个线程调用,以保护全局变量。这里要注意,lock只能锁定一个引用类型变量,即一个地址。
22.task/taskfactory:
后面补充,暂时用不到这么深入的技术。
23.Parallel:
后面补充,暂时用不到这么深入的技术。
24.有空的时候研究一下CLR、元数据、IL。