前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Array,List,Struct可能被大家忽略的问题

Array,List,Struct可能被大家忽略的问题

作者头像
MJ.Zhou
发布于 2018-01-04 08:24:43
发布于 2018-01-04 08:24:43
8250
举报
文章被收录于专栏:.NET开发那点事.NET开发那点事

Q1:

首先定义一个结构

public struct MyStruct { public int T; }

定义一个泛型List来存放结构体,然后访问第一个元素去修改T,输出T:

List<MyStruct> arrLis =new List<MyStruct>(){new MyStruct()};

arrLis[0].T = 100;

Console.WriteLine(arrLis[0].T);

大家猜是什么结果?

很遗憾不是100,arrLis[0].T = 100;VS提示该语句有错误。Cannot modify the expression because it is not a variable.

说修改的不是一个变量。

这是为什么呢?

关于这个问题我们首先来看一下List的源码

其实List[]被称做索引器。索引的实现其实类似属性,靠一对Get,Set方法来实现的。索引器其实只是C#的语法糖而已。那么很明显我们上面的语句其实只是调用了get_Item方法而已,且返回值MyStruct是个值类型。所以get_Item方法返回的是一个值(value)。你也许会说,那又怎么样,我为什么就不能修改这个值。很不辛,在.NET中值(value)是不能被修改的,只有变量(variable)才能够被修改,这就是为什么变量称之为”变量”了:)。

Q2:

再看下面的代码,我们修改一下,把泛型List改为Array数组。

MyStruct[] arrStr =new MyStruct[1]{new MyStruct()};

arrStr[0].T = 100;

Console.WriteLine(arrStr[0].T);

你是否觉得这次赋值语句也会报错?

其实不然,代码顺利通过编译,运行成功。

结果输出:100

这太奇怪啦,为什么把List改成Array就没有问题了呢。

让我们继续查看一下源码

看到没,对于一维数组的访问其实是访问到了这个GetValue方法。该方法的意思是使用typeReference去取到位于index位置的对象的引用,然后转换为Object返回。看来原因就在这里了,对于数组的[]索引器其实是返回了对象的一个引用(地址),也就是相当于我们使用Array[0]访问的是得到的是一个变量(variable),所以可以直接给内部的成员变量赋值。

对于这段源码也许不是那么好理解,不妨看看IL。

ldelema:将位于指定数组索引的数组元素的地址作为 & 类型(托管指针)加载到计算堆栈的顶部。

这就很清楚了,在IL里面也清楚的显示,操作的是对象的地址。

到这里,Array跟List索引访问的区别出来了,Array是返回了对象的引用,而List返回的就是对象的值(值类型对象就是内部的值,引用类型对象是引用的地址)。

Q3:

还没完,既然直接给赋值不行,那我用一个Set方法包装起来,去设置内部变量的值如何?

public struct MyStruct {

public int T;

public void SetT(int t)

  { T = t; }

}

改造一下,加了一个SetT方法。

把List初始化语句也改一下,去掉一些语法糖,因为我们要查IL,语法糖会影响我们的判断。

A:

List<MyStruct> arrLis = new List<MyStruct>();

var myStruct = new MyStruct();

arrLis.Add(myStruct);

arrLis[0].SetT(100);

Console.WriteLine(arrLis[0].T);

以上代码顺利通过。

输出:0

那为什么直接访问方法就可以呢。其实arrLis[0].SetT(100); 这也可以算是一个语法糖。上面A段代码到了IL层面其实就相当于下面B段代码,IL还是会用一个局部变量去接arrLis[0]返回的值。

B:

List<MyStruct> arrLis = new List<MyStruct>();

var myStruct = new MyStruct();

arrLis.Add(myStruct);

var temp = arrLis[0];

temp.SetT(100);

Console.WriteLine(arrLis[0].T);

不信我们查一下IL:

左边是A段代码,右边是B段代码:

这2段IL只有红线画出来的地方不一样,其实就是一个变量命名不一样而已。

Q4:

那上面A段代码输出为什么是0呢?

这个也很好理解,既然arrLis[0].SetT(100); 相当于var temp = arrLis[0]; 那么值类型赋值操作,其实是把右边的值(副本)赋值给了左边的变量,我们用SetT来修改T的时候只是在修改temp里面的T而已。这个不用多解释吧。

总结:

当我们在List里面使用值类型的时候一定要格外小心,特别是使用结构体的时候,因为从表象上来说更像一个引用类型(结构可以定义方法,成员变量等),不知不觉你就会用引用类型对象的惯用法去处理问题,说不定就掉坑了。所以结构体最好定义为不可变的。

参考:

why-can-struct-change-their-own-fields

what-is-the-difference-between-listt-and-array-indexers

internals-of-array

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2013-11-29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
编写高质量代码改善C#程序的157个建议[动态数组、循环遍历、对象集合初始化]
    软件开发过程中,不可避免会用到集合,C#中的集合表现为数组和若干集合类。不管是数组还是集合类,它们都有各自的优缺点。如何使用好集合是我们在开发过程中必须掌握的技巧。不要小看这些技巧,一旦在开发中使用了错误的集合或针对集合的方法,应用程序将会背离你的预想而运行。
aehyok
2018/08/31
1.7K0
编写高质量代码改善C#程序的157个建议[动态数组、循环遍历、对象集合初始化]
.NET基础加强笔记
3.Console.ReadLine,写入的为string类型;并只能输入一个。若想输入为汉子,之后Convert
洋仔聊编程
2019/01/15
1.2K0
老生常谈:值类型 V.S. 引用类型
我在面试的时候经常会问一个问题:“谈谈值类型和引用的区别”。对于这个问题,绝大部分人都只会给我两个简洁的答案:“值类型分配在栈中,引用类型分配在堆中”,“在默认情况下,值类型参数传值(拷贝),引用类型参数传引用”。其实这个问题有很大的发挥空间,如果能够从内存布局、GC、互操作、跨AppDomain传递等方面展开,相信会加分不少。这篇文章独辟蹊径,从“变量”的角度讨论值类型和引用类型的区别。
蒋金楠
2023/07/10
2810
老生常谈:值类型 V.S. 引用类型
C# 中比较实用的关键字,基础高频面试题!
在C#编程中关键字是构建逻辑和实现功能的基石,它承载着编程语言的语法规则和编程智慧。熟练掌握这些基础高频关键字对提升编程能力和面试表现至关重要,它们是日常开发和解决复杂问题的关键。
追逐时光者
2025/03/22
340
C# 中比较实用的关键字,基础高频面试题!
c#面试题汇总
下面的参考解答只是帮助大家理解,不用背,面试题、笔试题千变万化,不要梦想着把题覆盖了,下面的题是供大家查漏补缺用的,真正的把这些题搞懂了,才能“以不变应万变”。回答问题的时候能联系做过项目的例子是最好的,有的问题后面我已经补充联系到项目中的对应的案例了。
进击的猫思
2018/09/18
5.2K0
C#规范整理·集合和Linq
LINQ(Language Integrated Query,语言集成查询)提供了类似于SQL的语法,能对集合进行遍历、筛选和投影。一旦掌握了LINQ,你就会发现在开发中再也离不开它。
郑子铭
2023/08/30
2460
C#规范整理·集合和Linq
理解C#泛型运作原理
我们都知道泛型在C#的重要性,泛型是OOP语言中三大特征的多态的最重要的体现,几乎泛型撑起了整个.NET框架,在讲泛型之前,我们可以抛出一个问题,我们现在需要一个可扩容的数组类,且满足所有类型,不管是值类型还是引用类型,那么在没有用泛型方法实现,如何实现?
ryzenWzd
2021/03/07
7170
.NET面试题解析(04)-类型、方法与继承
做技术是清苦的。一个人,一台机器,相对无言,代码纷飞,bug无情。须梦里挑灯,冥思苦想,肝血暗耗,板凳坐穿。世界繁华竞逐,而你独钓寒江,看尽千山暮雪,听彻寒更雨歇。——来自《技术人的慰藉》
莫问今朝
2019/02/25
5330
.NET面试题解析(04)-类型、方法与继承
C#基础知识系列二(值类型和引用类型、可空类型、堆和栈、装箱和拆箱)
  之前对几个没什么理解,只是简单的用过可空类型,也是知道怎么用,至于为什么,还真不太清楚,通过整理本文章学到了很多知识,也许对于以后的各种代码优化都有好处。
aehyok
2019/02/25
1.1K0
.Net知识大全(个人整理)
本章内容适用于对.NET有一定基础的或者是想通过本文章对.NET基础知识记不清楚的朋友,可以通过本文章进行回顾。
用户7053485
2020/03/19
1.2K0
浅谈C#数组(一)
  如果需要使用同一类型的多个对象,可以使用数组和集合(后面介绍)。C#用特殊的记号声明,初始化和使用数组。Array类在后台发挥作用,它为数组中的元素排序和过滤提供了多个方法。使用枚举器,可以迭代数组中的所有元素。   如果需要使用不同类型的多个对象,可以使用Tuple(元组)类型。
全栈程序员站长
2022/09/07
9660
浅谈C#数组(一)
c#数组简单知识补充
通过执行该代码,当创建的数组是一维数组时,该数组对象会实现泛型接口,可以拥有linq查询的能力,但当不是一维数组时,此时的数据就不具有linq的能力,此时的数组对象不实现泛型接口。
Echo_Wish
2023/11/30
1600
《C#图解教程》读书笔记之六:接口和转换
  假设有如下一段代码,它使用Array类的一个静态方法Sort对一个未排序的int类型数组进行排序,并输出排序后的结果。
Edison Zhou
2018/08/20
4710
《C#图解教程》读书笔记之六:接口和转换
Unity性能调优手册10C#优化:GC,对象池,for/foreach,string,LINQ
翻译自https://github.com/CyberAgentGameEntertainment/UnityPerformanceTuningBible/
立羽
2023/11/27
1.7K0
Unity性能调优手册10C#优化:GC,对象池,for/foreach,string,LINQ
面向对象(四)-值类型与引用类型详解
类型被分为两种:值类型(整数,bool struct char 小数)和引用类型(string 数组 自定义的类,内置的类)。
孙寅
2020/06/02
9880
C# 数组基础
一、数组的基础知识 1、数组有什么用? 如果需要同一个类型的多个对象,就可以使用数组。数组是一种数组结构,它可以包含同一个类型的多个元素. 2、数组的初始化方式 第一种:先声明后赋值 int[] ar
郑小超.
2018/01/26
1.2K0
C#基本知识点-Readonly和Const的区别
  先解释下什么是静态常量(Const)以及什么是动态常量(Readonly)。   静态常量(Const)是指编译器在编译时候会对常量进行解析,并将常量的值替换成初始化的那个值。   动态常量(Readonly)的值则是在运行的那一刻才获得的,编译器编译期间将其标示为只读常量,而不用常量的值代替,这样动态常量不必在声明的时候就初始化,而可以延迟到构造函数中初始化。
zls365
2021/01/13
1.5K0
C#基本知识点-Readonly和Const的区别
《ASP.NET Core 高性能系列》致敬伟大的.NET斗士甲骨文!
 三年前,曾写过一篇文章:从.NET和Java之争谈IT这个行业,当时遭到某些自认为懂得java就了不起的Javaer抨击,
梁规晓
2019/07/24
9600
《ASP.NET Core 高性能系列》致敬伟大的.NET斗士甲骨文!
C#中的元组(Tuple)和结构体(struct)
在正常的函数调用中,一个函数只能返回一个类型的值,但在某些特殊情况下,我们可能需要一个方法返回多个类型的值,除了通过ref,out或者泛型集合可以实现这种需求外,今天,讲一下元组和结构体在这一方面的应用。
宿春磊Charles
2022/03/29
8990
C#中的元组(Tuple)和结构体(struct)
Python3 与 C# 扩展之~基础衍生
在线编程: https://mybinder.org/v2/gh/lotapp/BaseCode/master
逸鹏
2018/07/08
1.8K0
Python3 与 C# 扩展之~基础衍生
相关推荐
编写高质量代码改善C#程序的157个建议[动态数组、循环遍历、对象集合初始化]
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文