Unity之C#面试题(一)

内容将会持续更新,有错误的地方欢迎指正,谢谢!
 

Unity之C#面试题(一)
     
TechX 坚持将创新的科技带给世界!

拥有更好的学习体验 —— 不断努力,不断进步,不断探索
TechX —— 心探索、心进取!

助力快速掌握 面试题

为面试者节省宝贵的学习时间,避免困惑!

p1


一、请简述值类型与引用类型的区别

  1. 存储位置不同:
    值类型的数据存储在栈中(值类型嵌套在引用类型里时,跟随引用类型存储),而引用类型的数据存储在堆中,并且在栈中存储堆内存数据的地址。

  2. 效率不同:
    值类型不需要地址转换,存取快,引用类型需要进行地址转换,因为引用类型的变量存储的的是堆中的实例的地址,存取慢。

  3. 内存分配时机不同:
    值类型声明变量后,不管是否已经赋值,编译器都会为其分配内存,引用类型声明变量后,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其在堆上分配内存空间,当创建一个实例时,会分配堆上的空间,并把堆上的空间的地址保存到栈上分配的小片空间中。

  4. 释放方式不同:
    存储在栈中的值类型在使用完成后会自动释放,而存储在堆上的引用类型则靠GC进行垃圾回收。

  5. 继承不同:
    值类型隐式派生自System.ValueType,引用类型继承自System.Object。

  6. 默认值不同:
    值类型不能为null,默认初始化为该类型的默认值,引用类型默认初始化为null。

  7. 参数传递上的不同:
    值类型作为参数传递时,传递的是数据,引用类型作为参数传递时传递的是数据的引用。

  8. 赋值上的不同:
    当一个变量赋值给另一个变量时,值类型是会复制一个相同的新的值给另一个变量,引用类型是会将原变量数据的引用给新变量,当改变新对象的数据时也会对原对象照成影响,因为他们的引用是同一个。

  9. 值类型:
    struct、enum、bool、char、byte、sbyte、short、ushort、int、uint、long、ulong、float、double (6类:结构体、枚举、布尔、字符、整型、浮点型) 。

  10. 引用类型:
    class、interface、delegate、string、object、array、list(6类:自定义类,接口、委托、字符串、对象、数组)


二、什么是装箱和拆箱

1、什么是装箱拆箱

装箱就是值类型到引用类型的隐式转换,拆箱就是引用类型到值类型的显示转换。

2、装箱和拆箱的过程

  • 装箱过程:
    1、在堆上分配一个内存空间,这些内存空间主要用于存储值类型的数据
    2、将值类型变量的值复制到堆上新分配的内存空间上
    3、将堆上创建的对象的地址返回给引用类型变量

  • 拆箱过程:
    1、检查已装箱的值类型,确保它是给定值类型的一个装箱值
    2、将堆中存储的值拷贝到栈上的值类型实例中

3、 如何避免频繁的装箱拆箱

(1)、通过重载函数:Struct通过重载函数来避免装箱拆箱

struct Test
{
    int a;
    public override string ToString()
    {
        return "a的值是:" + a;
    }
    //如果struct没有重载ToString()方法,就会在struct实例调用它们时先装箱再调用,导致内存块重新分配性能损耗
}

(2)、通过泛型

interface ITest
{
    void Change(int a);
}
struct A : ITest
{
    private int aa;
    public A(int aa)
    {
        this.aa = aa;
    }
    public void Change(int aa)
    {
        this.aa = aa;
    }
}
struct B : ITest
{
    private int bb;
    public B(int bb)
    {
        this.bb = bb;
    }
    public void Change(int bb)
    {
        this.bb = bb;
    }
}

private void TestFunction<T>(T itest) where T:ITest
{
    itest.Change(100);
}

private void Start()
{
    B b = new B(10);
    TestFunction(b);
    //A和B都继承了ITest接口,当在实例化A或B的时候已经提前做了装箱操作,TestFunction在拿到参数的时候就不用再担心内部再次装箱和拆箱了。          
}

(3)、通过统一的接口提前装箱拆箱

interface ITest
{
    void Change(int a);
}
struct A : ITest
{
    private int aa;
    public A(int aa)
    {
        this.aa = aa;
    }
    public void Change(int aa)
    {
        this.aa = aa;
    }
}
struct B : ITest
{
    private int bb;
    public B(int bb)
    {
        this.bb = bb;
    }
    public void Change(int bb)
    {
        this.bb = bb;
    }
}

private void TestFunction(ITest itest)
{
    itest.Change(100);
}

private void Start()
{
    ITest test = new B(10);
    TestFunction(test);
    //A和B都继承了ITest接口,当在实例化A或B的时候已经提前做了装箱操作,TestFunction在拿到参数的时候就不用再担心内部再次装箱和拆箱了。          
}

4、装箱拆箱缺点

  • 由于装箱时生成的是全新的对象,会不断的分配和销毁内存,不但会大量消耗CPU,也同时增加了内存碎片,降低了性能。
  • 装箱和拆箱都会导致数据在堆和栈上进行拷贝,频繁的装箱拆箱操作会性能损失。

5、装箱使用情形

  • 调用含类型为Object的参数的方法时,该Object可支持任意类型,以便通用,当传入值类型的值就需要进行装箱。
  • 对于一个非泛型容器,同样为了保证通用,而将元素类型定义为Object,当值类型数据添加进容器的时候就需要装箱。


三、ref引用参数和out输出参数的区别?

  1. 使用ref型参数时,传入的参数必须先被初始化,对out型参数时,不需要先被初始化,但是在方法中需要对其完成初始化。

  2. 使用ref和out时,在方法的参数和执行方法时,都要加ref或out关键字,以满足匹配。

  3. out参数适用于需要返回多个返回值的时候,而ref参数则适用于需要在方法中修改调用方引用参数的引用的对象。

  4. out参数和ref参数它们都是按引用传递,而不是按值传递。

  5. ref和out关键字都让形参成为了实参的别名,所以在传递参数时必须是变量,而不能是表达式。


四、class与struct区别?

  1. 存储位置
    struct结构体是值类型,存储在栈上,class类是引用类型,对象存储在堆上,引用存储在栈上,结构体在声明的时候已经为其在栈上分配了内存,而类需要在用new为其分配堆上的内存。
  2. 新建对象
    class的对象需要通过new关键字创建,而struct可以用也可以不用new关键字来创建,当不用new关键字来创建时,用户不允许访问其方法、事件和属性。
  3. 构造函数
    struct不能定义无参构造函数,因为结构体的无参构造函数是自动定义且不能改变的,默认的无参构造函数会一直存在,默认无参构造函数不会因为定义了有参构造函数就消失,class默认有一个无参构造函数,当我们显示声明了一个构造函数时原来的默认构造函数没有了。struct可以自定义有参构造函数,但是需要在构造函数中初始化所有成员变量,而类不需要。
  4. 析构函数
    struct不能有析构函数,class可以有析构函数
  5. 继承
    struct不能从另一个struct或class继承,也不能作为struct和class的基类,而class可以继承其他的class,也可以作为其他类的基类,结构体和类都可以实现一个或多个接口。
  6. 默认访问访问符
    如果未指定访问指示符,对class而言,其成员默认是private,而struct而言,默认public。
  7. 初始值
    struct成员变量声明不能指定初始值,class可以指定初始值。
  8. 修饰符
    结构体类型是隐式密闭的,不能使用sealed和abstract修饰。结构体的成员不能被指定为抽象的、虚拟的或者保护的对象,因此结构体的成员不能使用abstract、virtual和protected修饰符,但是函数可以使用override关键字用于覆盖基类中的方法。


五、数组、ArrayList、List的主要区别?

  1. 数据的容量是固定的,当我们在实例化数组时必须指定长度,数组的空间是连续的,插入和删除元素效率低,而ArrayList或List的容量可根据需要自动扩充、修改、删除或插入数据。
  2. 数据可以是多个维度的,而ArraList和List只有一个维度。
  3. ArrayList中存储的对象是Object类型,在存取的时候需要经过装箱拆箱处理,会带来很大的性能消耗,同时也会造成数据不安全的问题隐患。
  4. 在声明List集合时需要声明集合内的数据类型,只能存储指定的类型。


六、private、public、protected、internal、sealed区别?

  1. private 是完全私有的,只有在类自己里面可以调用,在类的外部和子类都不能调用,子类也不能继承父类的private的属性和方法。
  2. public 对任何类和成员完全公开,无访问限制。
  3. protected 只有自己和自己的子类可以调用,子类可以继承父类的protected的属性和方法。
  4. internal同一应用程序集内部可以访问。
  5. sealed声明类就不能继承,声明方法就不能被重写。
  6. 访问权限private<protected<internall<public。


七、描述Interface与抽象类的区别?

1、什么是抽象类


抽象类是对现实世界对象的抽象化表示,通常仅定义它具有的特征,功能、关系,但不去做具体的实现,即为抽象类。

抽象类特征:

  1. 抽象方法只能出现在抽象类中,且不包含任何实现,但是抽象类中可以包含普通方法。
  2. 抽象类不能实例化,只能实例化它的派生类。
  3. 抽象类和抽象方法需要使用abstract关键字。
  4. 子类实现父类的抽象方法时,需要添加override关键字。
  5. 抽象类可以派生自一个抽象类,可以重写基类的抽象方法,也可以不重写,如果不重写的话则其派生的普通类必须重写他们。

2、什么是接口


接口就像一种行为规范,是为后续代码编写与程序开发制定的一种协定。

接口类特征:

  1. 接口的成员不能有任何的实现
  2. 接口不能被实例化
  3. 接口的成员不能有访问修饰符,默认为public
  4. 接口可以派生自一个接口,可以实现父接口中的方法,也可以不实现,如果实现的话,其实现接口的子类必须将接口的所有成员函数实现
  5. 子类实现接口的方法时,不需要任何关键字,直接实现即可
  6. 接口中只能包含属性、方法、索引器和事件
  7. 一个类可以实现多个接口,且每个接口中所有的成员必须都实现
public delegate void DelegateTest();
public interface ITest //只能包含非静态成员函数,隐式public,但不允许访问修饰符
{
    void Method(string a); //方法
    string Property //属性
    {
        get; set;
    }
    event DelegateTest EventTest; //事件,需要先定义一个委托
    int this[int index] //索引器
    {
        get; set;
    }
}

3、抽象类和接口的区别


相同点:

  • 都不能实例化
  • 包含未实现的方法声明
  • 派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员

不同点:

  • 接口用于规范,用于为不相关的类提供通用的功能,抽象类用于共性,用于关系密切的对象
  • 抽象类是类,故只能单继承,接口可一次实现多继承
  • 接口中只能声明方法、属性、事件、索引器,不能定义字段,不能实现方法,抽象类则可以,它是一个不完全类,抽象类里面有抽象的方法,属性,也可以有具体的方法和属性
  • 接口成员必须是公共的,且不写public,抽象类中的非抽象方法成员访问权限任意,但是抽象方法权限不能是private
  • 接口可作用于值类型和引用类型,抽象类只能作用于引用类型,struct可以实现接口,不能继承类

一个类继承了某个抽象类表示它“是什么”,实现了某个接口表示它“有什么功能”或者会做什么事,但不表达“如何做”
比如:燕子(具体类)是鸟(抽象类),会飞(接口)。C#中不支持多继承,即燕子只能是鸟,不会是其他东西了;但可以有多个功能,做很多事,比如会飞(IFly),会吃(IEat)。



TechX —— 心探索、心进取!

每一次跌倒都是一次成长

每一次努力都是一次进步

END
感谢您阅读本篇博客!希望这篇内容对您有所帮助。如果您有任何问题或意见,或者想要了解更多关于本主题的信息,欢迎在评论区留言与我交流。我会非常乐意与大家讨论和分享更多有趣的内容。
如果您喜欢本博客,请点赞和分享给更多的朋友,让更多人受益。同时,您也可以关注我的博客,以便及时获取最新的更新和文章。
在未来的写作中,我将继续努力,分享更多有趣、实用的内容。再次感谢大家的支持和鼓励,期待与您在下一篇博客再见!
  • 27
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沐沐森的故事

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值