给引用类型的值类型字段赋值需要装箱吗?

C#.NET笔试题基础篇题目:

C#进阶篇+.NETCore面试题合集请点击跳转文章:

C#.NET笔试题基础篇答案:

通用语言规范。不同语言语法的不同。每种语言都有自己的语法,.NET通过CLS提供了公共的语法,然后不同语言翻译生成对应的.NET语法。 CLR:Common Language Runtime 公共语言运行时,就是GC、JIT等这些。有不同的CLR,比如服务器CLR、Linux CLR(Mono)、Silverlight CLR(CoreCLR)。相当于一个发动机,负责执行IL。

Core面试题合集请点击跳转文章:

Core面试题合集请点击跳转文章:

托管代码总我们不必担心内存泄漏,这是因为有了?

MVC是典型的平行关系,没有说谁在上谁在下的关系,模型负责业务领域的事情,视图负责显示的事情,控制器把数据读取出来填充模型后把模型交给视图去处理。而各种验证什么的应该是在模型里处理了。它强制性的使应用程序的输入、处理和输出分开。MVC最大的好处是将逻辑和页面分离。

46.能用foreach遍历访问的对象的要求

程序集包含模块,而模块又包括类型,类型下有成员,反射就是管理程序集,模块,类型的对象,它能够动态的创建类型的实例,设置现有对象的类型或者获取现有对象的类型,能调用类型的方法和访问类型的字段属性。它是在运行时创建和使用类型实例。

48.ORM中的延迟加载与直接加载有什么异同?

延迟加载(Lazy Loading)只在真正需要进行数据操作的时候再进行加载数据,可以减少不必要的开销。

50.23种设计模式分别叫什么名称,如何分类?

创建型,行为型,结构型;

1.单例模式 2.工厂模式 3.建造者模式 4.原型模式 5.工厂方法模式

1.策略模式 2.模板方法模式 3.观察者模式 4.迭代子模式 5.责任链模式 6.命令模式 7.备忘录模式 8.状态模式 9.访问者模式 10.中介者模式 11.解释器模式

1.适配器模式 2.装饰器模式 3.代理模式 4.外观模式 5.桥接模式 6.组合模式 7.享元模式

}

先思考一些经典问题你就有体会了。

首先要明确的是,结构是值类型,类是引用类型。

问题:要求定义一个方法,接受一个int类型参数,要求方法返回后作为实参传递进来的变量值增加1。注意我的要求是让变量本身加一,而不能通过返回值赋值。

你不假思索,这还不简单,一顿操作,写出了如下代码:

然后我要求你检查一下,你会发现n的值并没有变,你大呼上当,开始怀疑人生。

这个时候我又问了一个问题。假设有如下类的定义(注意它是个class):

现在要求实现另一个方法,这次接受这个TheClass类型的参数,然后给它的N加一。

你一顿操作,生怕有诈,但还是写出了几乎同样道理的方法:

你自己检查了一下,N居然变了。你开始怀疑是不是发现了.NET的bug。

当然不是,因为事情本身就是这样的。

接下来是一个更加经典的问题。

要求实现一个方法,方法返回后作为参数的两个变量的值相互交换。

你又不假思索一顿操作,写出如下代码:

看到结果时,你顿时觉得晴天霹雳,a和b还是不变。你苦思冥想,怎么也想不出方法的其他实现方法。

首先,要实现这个方法,思路确实是这样没有问题。因此真正的问题就出现在参数的传递上。

当我们声明一个值类型变量,值类型的值被推到栈中,我们的变量相当于直接保存了值类型的值本身。

而一个引用类型的变量,当我们在堆里面new了一个对象,栈上的变量实际上保存的是一个指向堆中对象的引用,或者说它真的就像是C++的指针(或者引用)那样。

当变量相互赋值时,值类型的变量直接拷贝了一份值。当变量离开作用域,就会从栈上弹出来。

而引用类型不会在堆中构造一个新的对象,而是只复制引用(当然另有方法可以拷贝一个新对象),使得被赋值的变量引用同一个对象。即使某个引用类型变量离开作用域不能被使用,但是堆中的对象不一定马上就被销毁了,因为有可能还有其他变量引用,堆上的对象到底合适被销毁涉及到一个更加复杂的问题,对于.NET的托管代码我们实际上大多数时候不需要考虑这个问题。

思考下面的代码,就能发现值类型和引用类型在复制时的行为是不一样的。

对于参数的传递,不要想得很神奇以为是把一个变量丢进了方法的作用域。你可以认为参数传递实际上就相当于用实参给方法中的形参赋值,因此上面的讨论对参数传递依然成立。

对于值类型参数在传递时的默认行为,被称为按值传递(pass by value),而对于引用类型,默认情况下也是按值传递,引用本身也是一个“值”(可以认为他们真正保存的东西是一个指向堆的地址),引用指向的东西并没有被复制。(经评论区朋友指出,这一段修改了一些错误的描述,感谢)

那么究竟如何解决这个问题。这里就要用到。ref关键字表明这个参数需要按引用传递(pass by reference)(注意切勿将引用本身和“按引用传递搞混了,我写上一段中的错误就有这样的原因)。

前面的方法这样修改即可:

调用时也需要在参数前加上ref。这个时候你在执行之前的代码,就会发现变量的值按照我们的期望交换了。

ref参数不会再直接拷贝变量的值进来,而是相对于给原本的变量在方法的作用域内取了一个别名,形参现在也是引用,现在形参指哪打哪儿,形参上的操作会直接反应到传递进来的实参上。对于C/C++,它们就真的像是指针和引用一样。实际上返回值也可以为ref。

现在你表示,我完全懂了,没有人比我更懂引用和值了。

那么现在我要求你针对TheClass类实现这个交换方法。

你已经胸有成竹,甚至提前识破了我的陷阱。你现在可能认为:类是引用类型,传递的本来就是引用,所以肯定不需要ref就行了!于是:

结果你一检查N的值。又开始怀疑人生了。并没有变。你心中大喊为什么。

首先,确实类类型参数传递进来的是引用而不是对象本身。但是仍然发生了一个“值”的复制,这个值是引用类型变量中保存的引用、地址。形参a和b拿到了引用,我们交换也仅仅交换了方法作用域内a和b的值,方法一旦完成,a和b也就没用了,方法外的两个变量仍然引用着原本引用的对象。

这个时候,我们仍然需要ref关键字,如果TheClass类型的交换方法仍然使用ref参数传递,你就会发现结果是正确的。

你又会想问,我擦,不是本身就是引用吗,那引用按引用传递是啥意思???

你问出这个问题实际上是自己回答了问题,答案就是引用引用的引用(???)。或者可以理解为,指向指针的指针。

当把TheClass也按引用传递时,形参a和b不会直接复制实参所保存的引用,而是直接引用到外面引用的地址,因此交换时实际上就把外面的变量保存的引用也相互交换了,也就达到了目的。

因此还有一个结论就是,实际上按引用传递和参数类型是引用类型还是值类型是无关的(只是恰好名字容易产生混淆)。

实际上,值类型和引用类型在不同的需求下有着各自的作用。

和ref关键字配套的和

}

我要回帖

更多关于 引用传递和值传递的区别 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信