C#类

1:在C#中,string str = null 与 string str = “” 请尽量使用文字或图
象说明其中的区别。

string str=null,表示一个空引用,没有占用了空间,

string str= " ",表示一个空串,被实列化了,占用了内存空间,

2:简述类和结构的相同点和不同点。并用代码举例。

类:
类是引用类型在堆上分配,类的实例进行赋值只是复制了引用,都指向同一段实际对象分配的内存
类有构造和析构函数
类可以继承和被继承
结构:
结构是值类型在栈上分配(虽然栈的访问速度比较堆要快,但栈的资源有限放),结构的赋值将分配产生一个新的对象。
结构没有构造函数,但可以添加。结构没有析构函数

3:什么是拆箱和装箱?举例说明

“引用类型”转换为“值类型”就是拆箱,“”值类型转换为“引用类型”就是装箱

拆箱:

object objValue = 4;

int value = (int)objValue;

上面的两行代码会执行一次装箱操作将整形数字常量4装箱成引用类型object变量objValue;然后又执行一次拆箱操作,将存储到堆上的引用变量objValue存储到局部整形值类型变量value中。

同样我们需要看下IL代码:

.locals init (

  [0] object objValue,

  [1] int32 'value'

) //上面IL声明两个局部变量object类型的objValue和int32类型的value变量

IL_0000: nop

IL_0001: ldc.i4.4 //将整型数字4压入栈

IL_0002: box [mscorlib]System.Int32  //执行IL box指令,在内存堆中申请System.Int32类型需要的堆空间

IL_0007: stloc.0 //弹出堆栈上的变量,将它存储到索引为0的局部变量中

IL_0008: ldloc.0//将索引为0的局部变量(即objValue变量)压入栈

IL_0009: unbox.any [mscorlib]System.Int32 //执行IL 拆箱指令unbox.any 将引用类型object转换成System.Int32类型

IL_000e: stloc.1 //将栈上的数据存储到索引为1的局部变量即value

装箱:

我们再看装箱时都会发生什么事情,下面是一行最简单的装箱代码

object obj = 1;

这行语句将整型常量1赋给object类型的变量obj; 众所周知常量1是值类型,值类型是要放在栈上的,而object是引用类型,它需要放在堆上;要把值类型放在堆上就需要执行一次装箱操作。

这行语句的IL代码如下,请注意注释部分说明:

.locals init (

  [0] object objValue

//以上三行IL表示声明object类型的名称为objValue的局部变量

IL_0000: nop

IL_0001: ldc.i4.s 9 //表示将整型数9放到栈顶

IL_0003: box [mscorlib]System.Int32 //执行IL box指令,在内存堆中申请System.Int32类型需要的堆空间

IL_0008: stloc.0 //弹出堆栈上的变量,将它存储到索引为0的局部变量中

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的优越性是在:
第一:他不需要每次都去分配内存空间。所以系统就没有必要去处理垃圾;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值