C# 面试题整理

1. byte a = 'a'; byte b = -127; byte c = "ab" ; byte e =256; 这些变量哪些是正确?哪些是错误的?错误的原因是什么?

首先要知道 byte 在 C# 的范围是 0~255。所以变量 b 和 e 是错误。

变量 c 是错误的,字符串类型没办法转成 byte 类型。

变量 a 是无法将 char 隐式转换为 byte,正确定义加一个 byte a = (byte) 'a'; char a 会转为对应ASCII码97。

比如说下面代码是没问题的

byte a=2;
char b='a';
Console.WriteLine(a+z);  //输出 99

2. byte a = 255; a +=5; a 的值是多少?

byte 的取值范围是 0~255 ;a += 5 会溢出,结果为 4 。首先将 255 转化二进制为 11111111 ,5 的二进制为 101,然后根据加法运算如下:

11111111
        101
--------------
00000100         //转化为十进制就是 4

3. int? 和 int 有什么区别?

int? 为可空类型,等同于 Nullable<int> 表示其基础值类型正常范围值内,再增加一个 null 值,属于引用类型。它的默认值为 null。

int 默认为0,属于值类型。

4. 写个方法判断一个整数是否2的N次方。

  • 取模,用num % 2 == 0 可以判断。

  • & 运算,两个数对应二进位都为 1 时才为 1,否则为 0。2 的 n 次方有一个特性,在二进制中,只有 1 个 1。n  & (n - 1) 如果等于0,那么 n 一定是 2 的 n 次方,代码如下

bool IsPower(int num)
{
    int result = num & (num - 1);

    return result == 0;
}

5. 求一个整数是2的几次方?

int Seq(int num)
{
    int n = 1;
    while (num / 2 != 1)
    {
        num = num / 2;
        n += 1;
    }

    return n;
}

6. short s = 1; s = s+1; 和 short s = 1; s += 1; 哪个是正确,哪个错误?为什么?

s = s + 1; 先进行右边运算,1 默认是 int,s + 1 的返回值是 int , 然后再将右边得出的结果赋值给 s ,这时是不能将 int 类型隐式转换为 short 类型的。所以要加一个强制转换 s = (short)(s + 1) 。

而 s += 1;等价于 s = (short)(s + 1) ,它做了数据类型的转型,所以编译成功。

7. 如下代码,求 n 的值?

public class Class1
{
    public static int n = 0;

    static Class1()
    {
        n++;
    }

    public Class1()
    {
        n++;
    }
}
Console.WriteLine("{0}", Class1.n);  //1
Class1 class1 = new Class1();

Console.WriteLine("{0}",Class1.n);  //2

静态构造方法只会调用一次,而实例化几次就会运行几次。

class A
{
    public static int x;
    static A()
    {
        x = Program.y + 1;
    }
}

class Program
{
    public static int y = A.x + 1;


    static Program() { }
    
    static void Main(string[] args)
    {
        Console.WriteLine("x={0},y={1}", A.x, y);
		//输出 x=1,y=2
        Console.Read();
    }
}

 8. 下面代码输出的结果是多少?

Console.WriteLine(Math.Round(11.5));    //12
Console.WriteLine(Math.Round(11.2));    //11
Console.WriteLine(Math.Round(11.7));    //12
Console.WriteLine(Math.Round(-11.5));   //-12
Console.WriteLine(Math.Round(-11.2));   //-11
Console.WriteLine(Math.Round(-11.7));   //-12

9. i++ 和 ++i 有什么区别。

i++ 是先赋值,再自增;++i 是先自增,再赋值。

10. 如何用最有效且快速的计算出2乘以8等于多少

使用位运算左移(<<)比较快,但这还要加一个前提乘数必须是 2 的次方,这样才能成立。解题的过程是将 2 转化为二进制 0000 0000 0000 0010 ,然后左移 1 位  0000 0000 0000 0100 转化成十进制就是 4;左移的位数刚好是乘数的次方。8 刚好是 2 的 3 次方,所以 2 乘以 8 相当于 2<<3。 

11. 什么是装箱和拆箱

装箱就是隐藏地将一个值 类型转换成引用类型,如:

int i = 1;
object o = i;

 拆箱就是将引用类型转换成值类型,如:

int i = 1;
object o = i;
int n = (int)o;

12.值类型和引用类型的区别

  • 分配不同

    值类型存储在栈中,引⽤类型存储在堆中

  • 释放方式不同

    值类型在作用域内结时,会被系统自动释放,可心减少托管堆的压力;引用类型则靠GC,所以在性能上值类型优势比较明显。

  • 继承不同

    引用类型继承Object,且也可以被其它类继承。值类型不能作为基类,但是可以继承或者多继承接口。另外,值类型是继承ValueType,而引用类型不会继承ValueType。

  • 传参方式不一样

    值类型在传参的时候是复制内存的数据,而引用类型通过它们的引用来查找的,所以传参是共享一个内存地址。

  • 初始化行为不同

    引用类型需要通过 new 来创建对象。

其它,可以自行补充,至少回答3点吧。

13.new关键字的作⽤?

运算符:创建对象实例

修饰符:隐藏掉基类⽅法

public class Class1
{
    public void Add()
    {
        Console.WriteLine("Hello world!");
    }
}

public class Class2 : Class1
{
    public new void Add()
    {
        Console.WriteLine("Hello C#!");
    }
}

 约束:可以约束泛型类型,如:

public class Repository<T> where T : class, new()
{

}

使用 Repository<T> 的时候 T 的类型必须类且有空构造函数

14.using关键字的作⽤

  • 引用命名空间;为命名空间或类型创建别名。

  • 释放资源,实现了IDisposiable的类在using中创建,using结束后会⾃定调⽤该对象的Dispose⽅法, 释放资源。

15. const 和 readonly 的区别

  1. const 不能用 static 修饰,只能通过类来访问;readonly 由实例对象来访问,可以用 static 来修饰。

  2. const 只能定义为值类型和 string 类型,readonly 没有限制类型。

  3. const 必须初始化值,在编译时会进行解析,readonly 可以在声明时和构造函数中进行初始化,不同的构造函数可以实现不同的初始值。

  4. const 可以定义为字段和局部变量,readonly 则只能定义字段

16.字符串中string str=null和string str=""和string str=string.Empty的区别?

这些的区别在于初始化对象内存分配上,str=null 还未分配内存空间,str="" 和 str=string.Empty 分配了内存空间,且初始值都是""; Empty 是 static readonly 修饰的字段。 

string str = string.Empty;
string str1 = "";
string str2 = null;

Console.WriteLine(ReferenceEquals(str, str1));//True
Console.WriteLine(ReferenceEquals(str, str2));//False
Console.WriteLine(ReferenceEquals(str1, str2));//False

可以通过"即时"面板查看内存分配情况:

&str
0x0000000f6f77e4d0
    *&str: 0x0000015c00001360
&str1
0x0000000f6f77e4c8
    *&str1: 0x0000015c00001360
&str2
0x0000000f6f77e4c0 //开辟了栈空间,完成初始化对象
    *&str2: 0x0000000000000000   //未分配堆空间,即还未有值

17.什么是委托,主要用在哪里?

委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。~摘自百度百科~

主要用于事件和函数回调。

18.什么是强类型,什么是弱类型?

强类型是在编译的时候就要确定数据类型,在执行的时候不能更改;而弱类型是在运行的时候才能确定数据类型。两者没有好坏,强类型在编译的时候确认数据类型安全性相对高一些,而且效率高。

19. ref 和 out 的区别

  • ref 传参时,传入的参数必须先被初始化;out 不必  
int i;
Foo(ref i);//错误
Foo(out i);//通过
  • ref 传进去的参数在函数内部可以直接使用,而 out 不可以
public void Foo(ref int i)
{
    int n=i;//通过,因为 i 已经初始化值
}
public void Foo(out int i)
{
    int n=i;//错误,已初始化不可直接使用
}
  • ref 传进去的参数在函数内部可以不被改变,但 out 必须在函数体结束前进行赋值。  
public void Foo(ref int i)
{
    Console.WrilteLine(i);//通过
}
public void Foo(out int i)
{
    i = 10; //这里必须赋值,不然会报未赋值
    Console.WriteLine(i);
}

 20.Equals 和 == 的区别

 对于值类型,两者比较的是变量的值

int n = 5;
int m = 5;

Console.WriteLine("n == m :{0}", n == m);  //True
Console.WriteLine("n.Equals(m) :{0}", n.Equals(m)); //True

对于引用类型,如果重写了 == 操作符,则比较的值,或者比较的是引用地址,看两段代码

string str = "Hello C#!";
string str1 = "Hello C#!";

Console.WriteLine("str == str1 :{0}", str == str1); //True
Console.WriteLine("str.Equals(str1) :{0}", str.Equals(str1)); //True

可以知道 string 的 == 与 Equals 返回都是 True ,通过反编译的工具找到到代码如下:

public static bool operator ==(String? a, String? b)
{
    return a.Equals(b);
}

再看另外一段代码

public class Student
{
    public int Id { get; set; }

    public string Name { get; set; }

    public Student(int id, string name)
    {
        Id = id;
        Name = name;
    }
}

Student stu = new Student(10086,"程序员");
Student stu0 = new Student(10086,"程序员");

Console.WriteLine("stu == stu0 :{0}", stu == stu0); //False
Console.WriteLine("stu.Equals(stu0) :{0}", stu.Equals(stu0)); //False

另外,两个对像值相同(x.Equals(y)==true),其HashCode也是相同的。

21. as 和 is 的区别

as 在转换的同时判断兼容性,如果无法转换返回 null ,as 转换是否成功判断的依据是结果是否为 null

is 只是做类型兼容性的判断,并不执行真正的类型转换,返回 true 或 false

效率上 as 比 is 更高,as 只做一次类型兼容检查

22. 静态成员和非静态成员的区别

  • 静态变量使用 static 修饰符进行声明,不带有 static 声明的变量为非静态变量

  • 静态变量在类被实例化时创建,通过类进行访问;非静态变量在对象被实例化时创建,通过对象进行访问

  • 静态函数的实现里不能使用非静态成员,包含非静态变量和非静态函数;但非静态函数可以使用静态成员

23. struct 和 class 的区别

  • struct 是值类型;class 是引用类型

  • 不能声明无参数构造函数。每个 struct类型已经提供了一个隐式的无参构造函数,该构造函数会对声明的字段或属性进行设置默认值。从 C# 10 开始可以声明无参数构造函数

  • 不能对实例字段或属性时进行初始化。但可以在声明中初始化静态或常量字段或静态属性。从 C# 10 开始可以对实例字段或属性进行初始化

  • struct 类型的构造函数必须对所有实例字段或属性进行初始化。

  • struct 类型不能从其他 class 或 struct 类型继承,也不能作为 class 基础类型。不过,struct 可以继承接口。

  • 不能在 struct 类型中声明终结器(即:析构函数)。

  • struct 使用方式,一种通过 new 来创建;另外,也可以使用值类型那样来定义。

  • struct 是深度拷贝。

24. 重写和重载的区别

重写:派生类(子类)继承基类(父类),而子类与父类中的方法名、参数、返回类型相同,就称子类重写父类的方法。父类方法必须用关键字 virtual 修饰,子类必须用关键字 override 修饰。  

/// <summary>
/// 父类
/// </summary>
public virtual void Read()
{
    
}

/// <summary>
/// 子类重写
/// </summary>
public override void Read()
{

}

 重载:在同一个类中,方法名必须相同,参数必须不相同,返回类型可以不相同

public void Test(int x,int y){} 
public void Test(int x,ref int y){} 
public void Test(int x,int y,string a){}

 25. 构造函数能否被重写

构造函数不能被继承,所以不能被重写,只能被重载。

26. 抽象类和接口的区别

相同点:

  • 都不能被实例化

  • 可以被继承

  • 可以包含方法的声明

  • 子类必须实现未实现的方法

不同点:

  • 接口不能包含实例字段

  • 接口支持多重实现,抽象类只能单一继承

  • 接口中没有实现方法体,只负责功能定义,即一个行为的规范;抽象类中的方法可以实现,也可以不实现,有抽象方法的类一定要用 abstract 修饰。

  • 接口没有构造方法,抽象类有构造方法

  • 抽象类⽤abstract修饰、接⼝⽤interface修饰

  • 接口中方法默认是public,抽象类可以用 protected 修饰

  • 接口可以继承接口,但不可以继承类,抽象类可以实现接口,而可以继承实现类,如果实现类有构造函数,抽象类必须实现。

public class Person
{
    public string Name { get; set; }
    
    public Person(string name)
    {
        Name = name;
    }
}

public abstract class Annimal : Person
{
    protected Annimal(string name) : base(name)
    {
    }
}

27. 继承的好处

对父类成员进行重用,增加代码的可读性、灵活性。

28. 当使⽤new B()创建B的实例时,x 和 y 分别输出什么?

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);
    }
}

输出:x=1,y=0

这题主要考查的是实例化执行的顺序。

不考虑继承的执行顺序:

  1. 静态字段

  2. 静态构造方法

  3. 实例字段

  4. 实例构造方法

存在继承关系的执行顺序

  1. 子类的静态字段

  2. 子类的静态构造函数

  3. 子类的实例字段

  4. 父类的静态字段

  5. 父类的静态构造方法

  6. 父类的实例字段

  7. 父类的实例构造函数

  8. 子类的实例构造函数

29. 数组和 string 有没有Length()这个方法?

都没有Length()这个方法,只有Length属性。

30. swtich 表达式可以用什么类型? string 类型可以?

swtich 表达式是一个整型(byte、int、long等),字符或字符串类型。

31. using的有哪些用法?

引用命名空间,也可 using 别名;也可以释放资源,实现了 IDisposiable 的类在 using 中创建,using 结束后会自动释放该对象的Disposiable();

先整理这些。

最后,祝大家学习愉快!

  • 13
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值