重学c#————struct
前言
简单整理一下struct。
正文
struct
对于struct 而言呢,我们往往会拿class作为对比,但是呢,我们在初学阶段用class来替代struct,struct的存在感越来越低了。
那么是什么原因使我们经常使用struct呢?我感觉很简单的一句话就是struct能做的class都能做,struct不能做的,class 也能做,这就是问题关键了。
那么先来看下他们的对比:
1、结构是值类型,它在栈中分配空间;而类是引用类型,它在堆中分配空间,栈中保存的只是引用。
2、结构类型直接存储成员数据,让其他类的数据位于堆中,位于栈中的变量保存的是指向堆中数据对象的引用。
结构不支持继承。
结构不能声明默认的构造函数。
结构类型中不能设置默认值。
从第二点中可以明白结构类型中,不一定存储的一定是值,还可能是引用,这就打破了初学的时候误以为结构类型只能存储值类型,还可能是引用对象的引用,如下:
static void Main(string[] args)
{
var parent = new Parent(30,"张大大");
var zhangsan = new Student(1,"张三",parent);
zhangsan.age = 10;
}
struct Student {
public Student(int age, string name,Parent parent)
{
this.age = age;
this.name = name;
this.parent = parent;
}
public int age { get; set; }
public string name { get; set; }
public Parent parent { get; set; }
}
struct Parent {
public Parent(int age, string name)
{
this.age = age;
this.name = name;
}
public int age { get; set; }
public string name { get; set; }
}
在Student 结构中,我们也可以去复制引用。
第三点很好理解,第四点表示我们不能去自己声明默认构造函数。如:
public Student() {
}
那么我们什么时候使用struct呢?
那么要从struct 优点出发,struct 是值类型,当离开作用域的时候,那么对垃圾回收是有好处的。
同样,因为struct 是值类型,分配到堆上,如果值类型过大,这会大量占用到堆的空间,所以我们的数据比较下。
当有大量的赋值语句的时候,那么我们也应该避开struct,因为赋值值类型中将会拷贝全部,而不是引用。
根据上诉,实用场景为:
对于点、矩形和颜色这样的轻量对象,假如要声明一个含有许多个颜色对象的数组,则CLR需要为每个对象分配内存,在这种情况下,使用结构的成本较低;
从上总结出,struct可以在一些以数据为主的场景中使用,且数据量不大的情况。
struct 作为参数
在介绍readonly 之前,先介绍一下,和ref 还有out 其名的in,不是别的in哈。
static void Main(string[] args)
{
int readonlyArgument = 44;
InArgExample(readonlyArgument);
Console.WriteLine(readonlyArgument); // value is still 44
}
static void InArgExample(in int number)
{
// Uncomment the following line to see error CS8331
//number = 19;
}
这里的in 的作用是可以引用readonlyArgument,但是只读,不能修改number的值。那么这有什么用呢?我直接不设置值不就可以吗?或者说我起码设置一个readonly 这总行吧。
而我们知道in 有不能用于异步方法,赋值消耗也不大形参,那我要这个引用有啥用?关键就在于我们自定义的struct还是大有好处的,struct 是我们自定义的结构类型,这个比较大,那么这就是一个struct的突破点了,传值的时候可以传递struct。
下面介绍readonly 这个是为了安全,做为一个readonly,我们首先就要区分的是const,const 是编译性,而readonly是运行时。这个可以百度,在此就不做过多的介绍。
通过readonly struct 还有 in,那么可以创建防御性副本。
这里值得注意的是,官网提到这样一句话:
除非使用 readonly 修饰符声明 struct或方法仅调用该结构的 readonly 成员,否则切勿将其作为 in 参数传递。 不遵守该指南可能会对性能产生负面影响,并可能导致不明确的行为
官网给出了一个这样的例子:
int readonlyArgument = 44;
InArgExample(readonlyArgument);
Console.WriteLine(readonlyArgument); // value is still 44
void InArgExample(in int number)
{
// Uncomment the following line to see error CS8331
//number = 19;
}
并且说明了一段这样的话:
在首次检查时,你可能认为这些访问是安全的。 毕竟,get 访问器不应该修改对象的状态。 但是没有强制执行的语言规则。
它只是通用约定。 任何类型都可以实现修改内部状态的 get 访问器。
如果没有语言保证,编译器必须在调用任何未标记为 readonly 修饰符的成员之前创建参数的临时副本。
在堆栈上创建临时存储,将参数的值复制到临时存储中,并将每个成员访问的值作为 this 参数复制到堆栈中。
在许多情况下,当参数类型不是 readonly struct,并且该方法调用成员未标记为 readonly 时,这些副本会降低性能,
使得按值传递比按只读引用传递速度更快。 如果将不修改结构状态的所有方法标记为 readonly,编译器就可以安全地确定不修改结构状态,并且不需要防御性复制。
struct 作为出参
那么上面讨论了参数传递的问题,那么接下来讨论一下,参数返回的问题。
比如说返回了:
var a=getANumber();
private static int getANumber(){
var b=1;
return b;
}
那么其实这个a的值怎么获取的呢?是b的值赋值给a。
但是对于比较大的struct,用这种赋值的方式,就比较消耗cpu和内存了。
那么可以使用ref来返回。
var a=getANumber();
private static ref int getANumber(){
var b=1;
return ref b;
}
这样b的引用传递给了a。
如果你希望返回的参数不可改变,那么你可以这样:
var a=getANumber();
private static ref readonly int getANumber(){
var b=1;
return ref b;
}
那么这个时候有人就奇怪了,为啥ref还要 readonly 这东西呢?
举个例子:
public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
for (int i = 0; i < matrix.GetLength(0); i++)
for (int j = 0; j < matrix.GetLength(1); j++)
if (predicate(matrix[i, j]))
return ref matrix[i, j];
throw new InvalidOperationException("Not found");
}
这个例子返回的是数组的一部分,如果改了这个值,那么数组里面的值不就改变了吗。
可能我这样说,加上官网这个例子不到位,可能没能表达明白。再来一个自己写的例子:
static void Main(string[] args)
{
var matrix =new Student[1];
matrix[0] = new Student(20,"张三",new Parent());
ref Student result =ref Find(matrix);
result.age = 10;
Console.WriteLine(matrix[0].age);
Console.WriteLine(result.age);
Console.ReadLine();
}
static ref Student Find(Student[] matrix )
{
return ref matrix[0];
}
struct Student {
public Student(int age, string name,Parent parent)
{
this.age = age;
this.name = name;
this.parent = parent;
}
public int age { get; set; }
public string name { get; set; }
public Parent parent { get; set; }
}
这里打印出来两个都是10。
如果给Find 加上readonly,那么要这样写
ref readonly Student result =ref Find(matrix);
Student 自然不能再进行赋值。
结
上述只是个人整理和一点点个人理解,如有不对望指出。下一节,整理c# 装箱和拆箱,介绍一下生命周期。
重学c#————struct的更多相关文章
- 重学c#系列——对c#粗浅的认识(一)
前言 什么是c#呢? 首先你是如何读c#的呢?c sharp?或者c 井? 官方读法是:see sharp. 有没有发现开发多年,然后感觉名字不对. tip:为个人重新整理,如学习还是看官网,c# 文 ...
- 重学hadoop技术
最近因为做了些和hadoop相关的项目(虽然主要是运维),但是这段经历让我对hadoop的实际运用有了更加深入的理解. 相比以前自学hadoop,因为没有实战场景以及良好的大数据学习氛围,现在回顾下的 ...
- Java集合类简单总结(重学)
java集合类简介(重学) 一.Collection(集合).Map接口两者应该是平行关系吧. 1.Map介绍 Map是以键值(key-value)对来存放的,2个值.通过key来找到value(例: ...
- 重学OpenGL(一)----工具篇
最近想开发一个小工具,需要用到3D,果断上OpenGL,借这个过程把OpenGL重学一遍. 工欲善其事,必先利其器,先把工具都搞好. [开发语言] 果断C+OpenGL,不解释. [开发环境] Min ...
- 重学C++ (1)
写在开头的话:这学期没有写太多的代码,终于把中英文两篇论文弄完了,趁着中间的空隙,想想找工作的处境.自己也定了自己的方向.不管学什么语言吧,每个语言都有自己的优势和使用的群体.只要自己是良马,终会有伯 ...
- 推翻自己和过往,重学自定义View
http://blog.csdn.net/lfdfhl/article/details/51671038 深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 A ...
- 重学C语言---01概述
1.什么是C语言 C语言是一种计算机程序设计语言,它既具有高级语言的特点,又具有汇编语言的特点.计算机语言是从第二次世界大战以后,经历了戏剧性的发展过程.从机器语言到汇编语言和高级语言.C语言是与硬件 ...
- 【重学计算机】操作系统D3章:存储管理
1. 存储管理的基本概念 逻辑地址:用户地址,从零开始编号 一维逻辑地址:(地址) 二维逻辑地址:(段号: 段内地址) 主存储器的复用方式 按分区:主存划分为多个固定/可变分区,一个程序占一个分区 按 ...
- 【重学计算机】操作系统D1章:计算机操作系统概述
1. 计算机软硬件系统 冯诺伊曼结构 以运算单元为核心,控制流由指令流产生 程序和数据存储在主存中 主存是按地址访问,线性编址 指令由操作码和地址码组成 数据以二进制编码 其他:参考<重学计算机 ...
随机推荐
- 两种方式,花五分钟就能构建一个 Spring Boot 应用
前言 Spring Boot 的好处自然不必多说,对于想要从事 Java 工作的朋友们来说,可谓是必学的技能. 在我看来,它的优势就是多快好省. 功能多,很多常用的能力都有集成: 接入快,简单的几行代 ...
- 现有以下关系型数据库中的表(见表4-20表4-21和表4-22),要求将具转换为适合Hbase存储的表并插入数据。
① createTable(String tableName, String[] fields) 创建表,参数tableName为表的名称,字符串数组fields为存储记录各个域名称的数组.要 求当H ...
- testng学习笔记-- beforesuit和aftersuit
一.定义 测试套件,主要关注执行顺序 套件:suit可以包含多个class 二.代码标签 三.运行结果
- 将ffmpeg编译为wasm版本且在浏览器中运行
2020年大前端技术趋势解读 原创 IMWeb团队 腾讯IMWeb前端团队 5天前
- BBR implements bbr-like limiter 限流
pkg/ratelimit/bbr/bbr.go:68 github.com/go-kratos // BBR implements bbr-like limiter.// It is inspire ...
- GDB查看内存命令(x命令) 用gdb查看指定地址的内存内容
GDB查看内存命令(x命令) - super119 - 博客园 https://www.cnblogs.com/super119/archive/2011/11/18/2254382.html 可以使 ...
- 慕课网金职位 Python工程师 百度网盘下载
百度网盘链接:https://pan.baidu.com/s/1xshLRO3ru0LAsQQ0pE67Qg 提取码:bh9f 如果失效加我微信:610060008[视频不加密,资料代码齐全,超清一手 ...
- Azure Terraform(八)利用Azure DevOps 实现Infra资源和.NET CORE Web 应用程序的持续集成、持续部署
一,引言 上一篇讲解到利用 Azure DevOps 将整个 Azure Web App,Azure Traffic Manager profile,Azure Storage Account,Azu ...
- (二)基于Netty的高性能Websocket服务器(netty-websocket-spring-boot)
@toc Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架,对比于BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高. 1.Netty为 ...
- 菜鸟初学Linux——Ubuntu系统中,用root权限进行复制粘贴操作
long long ago,积累了一些Linux的小知识,拿出来分享一下,希望以后能够在工作上带来一些帮助. 方法一 第一步:打开终端,在命令行里输入sudo nautilus 第二步:输入你的用 ...