1:在C#中,string str = null 与 string str = “” 请尽量使用文字或图
象说明其中的区别。
string str=null,表示一个空引用,没有占用了空间,
string str= " ",表示一个空串,被实列化了,占用了内存空间,
2:简述类和结构的相同点和不同点。并用代码举例。
类:
类是引用类型在堆上分配,类的实例进行赋值只是复制了引用,都指向同一段实际对象分配的内存
类有构造和析构函数
类可以继承和被继承
结构:
结构是值类型在栈上分配(虽然栈的访问速度比较堆要快,但栈的资源有限放),结构的赋值将分配产生一个新的对象。
结构没有构造函数,但可以添加。结构没有析构函数
3:什么是拆箱和装箱?举例说明
“引用类型”转换为“值类型”就是拆箱,“”值类型转换为“引用类型”就是装箱
拆箱:
|
上面的两行代码会执行一次装箱操作将整形数字常量4装箱成引用类型object变量objValue;然后又执行一次拆箱操作,将存储到堆上的引用变量objValue存储到局部整形值类型变量value中。
同样我们需要看下IL代码:
|
装箱:
我们再看装箱时都会发生什么事情,下面是一行最简单的装箱代码
|
这行语句将整型常量1赋给object类型的变量obj; 众所周知常量1是值类型,值类型是要放在栈上的,而object是引用类型,它需要放在堆上;要把值类型放在堆上就需要执行一次装箱操作。
这行语句的IL代码如下,请注意注释部分说明:
|
4:编程实现一个冒泡排序
using
System;
namespace
MySort
{
//先建立一个类,以后把所有排序方法都放到这个类里,
public
class
SumSort
{
//冒泡排序方法,从小到大排,虽然很多冒泡排序都是从大到小,
//可是我就想这么排,你能怎么着我。
public
void
PopSort(
int
[] list)
{
int
i, j, temp;
//先定义一下要用的变量
for
(i = 0; i < list.Length - 1; i++)
{
for
(j = i + 1; j < list.Length; j++)
{
if
(list[i] > list[j])
//如果第二个小于第一个数
{
//交换两个数的位置,在这里你也可以单独写一个交换方法,在此调用就行了
temp = list[i];
//把大的数放在一个临时存储位置
list[i] = list[j];
//然后把小的数赋给前一个,保证每趟排序前面的最小
list[j] = temp;
//然后把临时位置的那个大数赋给后一个
}
}
}
}
}
public
class
test
{
//这里给一组测试数据,打印输出看看排序方法的效果如何
static
void
Main()
{
int
[] arr = { 1, 4, 2, 43, 5, 61, 89, 34, 67, 32, 40 };
//把数据排序类实例化一下,然后调用方法。
//什么?还要实例,可我不想实例化怎么办?
//那也没有关系,把PopSort方法前加一个static,直接调用SumSort.PopSort(arr)就好了
SumSort mysort =
new
SumSort();
//来来来,大家按高矮排个队,矮的排前面高的排后面
mysort.PopSort(arr);
//真听话,看看大家都排第几位了
for
(
int
i = 0; i < arr.Length; i++)
{
Console.Write(
"第{0}位是{1}\n"
, i + 1, arr[i]);
}
Console.WriteLine();
}
}
}
5:编程实现一个递归方法
/// <summary>
/// 一列数的规则如下: 1、1、2、3、5、8、13、21、34求第30位数是多少, 用递归算法实现。(C#语言)
/// </summary>
/// <param name="pos"></param>
/// <returns></returns>
public
int
GetNumberAtPos(
int
pos)
{
if
(pos==0||pos==1)
{
return
1;
}
int
res = GetNumberAtPos(pos - 1) + GetNumberAtPos(pos - 2);
return
res;
}
6:说说目前学的集合有哪些?,每一种集合的特点以及使用场景
(1)ArrayList
ArrayList是List接口的可变数组非同步实现,并允许包括null在内的所有元素,相当于List < object>
(2)List < T >
泛型的List 类是一个不限长度的集合类型,它内部实际就是一个数组,初始长度是4,每次数组到达限制,就会把现有容量翻倍,它提供用于对集合进行搜索、排序和操作等方法
List是数组链表,数组链表访问快,复杂度O(1),但是添加删除复杂度O(n)
(3)LinkedList
LinkedList是List接口的双向链表非同步实现,并允许包括null在内的所有元素。
底层的数据结构是基于双向链表的,
LinkedList是指针链表,指针链表访问复杂度是O(n),但是添加删除很快O(1),如果对这个集合在中间的添加删除操作非常频繁的话,就建议使用LinkedList。
(4)Dictionary < K, V>
存储键值对的关联性集合,查询等操作速度很快,因为它的时间复杂度是O(1)
,单线程中推荐使用Dictionary,有泛型优势,且读取速度较快,容量利用更充分.
(5)Hashtable
Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似key/value的键值对
基本概念
Hashtable使用了闭散列法来解决冲突,它通过一个结构体bucket来表示哈希表中的单个元素,这个结构体中有三个成员:
(1) key :表示键,即哈希表中的关键字。
(2) val :表示值,即跟关键字所对应值。
(3) hash_coll :它是一个int类型,用于表示键所对应的哈希码。
哈希表的所有元素存放于一个名称为buckets(又称为数据桶) 的bucket数组之中
优点:
(1)在使用哈希表保存集合元素(一种键/值对)时,首先要根据键自动计算哈希代码,以确定该元素的保存位置,再把元素的值放入相应位置所指向的存储桶中。在查找时,再次通过键所对应的哈希代码到特定存储桶中搜索,这样将大大减少为查找一个元素进行比较的次数
(2)多线程程序中推荐使用Hashtable,对Hashtable进一步调用Synchronized()方法可以获得完全线程安全的类型
Dictionary< TKey, TValue> 是 Hashtable 的泛型版本,它们之间实现上区别不大,运行效率上有一些差别
Hashtable由于键值类型都object,所以涉及装箱拆箱操作,在添加数据的效率上要差一些,但是频繁使用数据时效率更高,HashTable的优点就在于其索引的方式,速度非常快。如果以任意类型键值访问其中元素会快于其他集合,特别是当数据量特别大的时候,效率差别尤其大。
7:变量被标记为 “const” 和readonly” 有何不同?
const 一个包含不能修改的值的变量。
readonly 允许把一个字段设置成常量,但可以执行一些运算,可以确定它的初始值。
常数表达式是在编译时可被完全计算的表达式。因此不能从一个变量中提取的值来初始化常量。
如果 const int a = b+1;b是一个变量,显然不能再编译时就计算出结果,所以常量是不可以用变量来初始化的。
readonly 是在计算时执行的,当然它可以用某些变量初始化。
readonly 是实例成员,所以不同的实例可以有不同的常量值,这使readonly更灵活。
readonly 关键字与 const 关键字不同。
1. const 字段只能在该字段的声明中初始化。
readonly 字段可以在声明或构造函数中初始化。因此,根据所使用的构造函数,readonly 字段可能具有不同的值。
2. const 字段是编译时常数,而 readonly 字段可用于运行时常数。
3. const 默认就是静态的,而 readonly 如果设置成静态的就必须显示声明。
4.const 对于引用类型的常数,可能的值只能是 string 和 null。
readonly可以是任何类型
const只能在初期就使用常量初始化好。对于每一次编译后的结果,const的值是固定的,而readonly的值是可以在运行的时候才确定值的
8:“out” 和 “ref” 参数有何不同?用代码举例
out:
static void Main(string[] args) { string str = "初始化赋值"; MethodOut(out str); Console.ReadKey(); } /// <summary> /// 只出不进 原来out参数在进入方法的时候,C#会自动清空它的一切引用和指向,所以在上面的out例子中,必需先要为str参数赋值 /// </summary> /// <param name="str"></param> public static void MethodOut(out string str) { str = "Hello World! ---out"; Console.WriteLine(str); }
ref:
//ref参数的参数必须最先初始化, static void Main(string[] args) { string str = "初始化赋值"; MethodRef(ref str); Console.ReadKey(); } /// <summary> /// 是有进有出 ref参数在使用前必需初始化,而out不需要。 /// </summary> /// <param name="i"></param> public static void MethodRef(ref string str) { str = "Hello World! --Ref"; Console.WriteLine(str); }
1、从上测试结果来看,可以验证ref 和 out 都是传递地址,都会改变具体的值,ref 是有进有出,Out 是 只出不进,
2、ref可以把参数的数值传递进函数,
3、out是把参数清空,就是说你无法把一个数值从out传递进去,out进去后,参数的数值为空(参数不能拿到具体数值),所以你必须初始化一次!
9:“StringBuilder” 和 “String” 有何不同?
Stringbuilder类是直接用于字符串操作的类,打个比方把
(1)string aa="123456";
(2)aa+="789";
(3)StringBuilder text=new StringBuilder("123456",12);
(4)text.Append("789");
如果你输出aa,和text 你会发现他们的输出内容是一样的。
但是aa的操作过程实际上是:首先在内存中分配一个地址空间,空间大小是6。
然后执行 aa+="789";的操作,该过程是连接字符串,“123456”和“789”并且在内存中重新分配地址。把aa 的内存地址指向 “123456789”的内存地址。
也就是说在内存中实际上是有两个空间北分配,第一的内存空间,在后来是由C#的垃圾处理机制来自动处理掉,
如果我们用3 4 句的程序来实现这个过程,那么他是没有再次分配内存空间的,
他就是在text的内存空间里进行了操作。这里要说明下StringBuilder在生命变量的过程中是可以我们自己来分配他的大小的,如果实际的内容超出内存空间,
他会自动翻倍。
通过上面的例子,我们可以知道 StringBuilder的优越性是在:
第一:他不需要每次都去分配内存空间。所以系统就没有必要去处理垃圾;