【C#学习笔记】值类型(2)

news2024/11/17 23:25:30

在这里插入图片描述

文章目录

  • Struct结构体类型
    • 为什么不推荐struct
  • 元组类型
  • 可为空的值类型
    • 从可为空的值类型转换为基础类型
    • 提升的运算符
    • 如何确定可为空的值类型
    • 为什么建议少用`T?`
      • 装箱和取消装箱


Struct结构体类型

结构类型(“structure type”或“struct type”)是一种可封装数据和相关功能的值类型 。 使用 struct 关键字定义结构类型:

public struct Coords
{
    public Coords(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; }
    public double Y { get; }

    public override string ToString() => $"({X}, {Y})";
}

结构类型具有值语义 。 也就是说,结构类型的变量包含类型的实例。 默认情况下,在分配中,通过将参数传递给方法并返回方法结果来复制变量值。 对于结构类型变量,将复制该类型的实例。

使用readonly关键字来保证结构体状态不可变。以此保证结构体内的成员不会修改结构体本身状态。正是由于它是值类型的,因此有可能会被修改,而我们又不希望它被修改。

为什么不推荐struct

这里也要点出为什么class往往优于struct,因为结构体是值类型的,一方面,结构体的赋值是通过复制整个结构体的值来实现的。这意味着当结构体的值较大时,赋值操作需要复制较多的数据,可能会消耗大量的内存和时间。

另一方面,结构体在作为参数传递给方法时,会进行值传递。这意味着传递的是结构体的一个副本,而不是原始的结构体实例。这会导致在方法内对结构体的修改不会影响到原始实例。

相比之下,使用类作为引用类型可以避免上述问题。类对象的赋值和传递只涉及引用的复制,而不是整个对象的复制。这样可以避免不必要的内存和时间消耗。而且类对象的传递是引用传递,这意味着方法内对对象的修改会影响到原始实例。

而一切的缺陷,本质根源于结构体是一个值类型,而class是引用类型。


元组类型

元组功能提供了简洁的语法来将多个数据元素分组成一个轻型数据结构。 下面的示例演示了如何声明元组变量、对它进行初始化并访问其数据成员:

(double, int) t1 = (4.5, 3);
Console.WriteLine($"Tuple with elements {t1.Item1} and {t1.Item2}.");
// Output:
// Tuple with elements 4.5 and 3.

(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
// Output:
// Sum of 3 elements is 4.5.

若要定义元组类型,需要指定其所有数据成员的类型,或者,可以指定字段名称。 虽然不能在元组类型中定义方法,但可以使用 .NET 提供的方法,如下面的示例所示:

(double, int) t = (4.5, 3);
Console.WriteLine(t.ToString());
Console.WriteLine($"Hash code of {t} is {t.GetHashCode()}.");
// Output:
// (4.5, 3)
// Hash code of (4.5, 3) is 718460086.

使用元组类型的情况通常用于接受函数多返回值。如果想要一个可变动的,带有方法的数据结构,类还是优于元组的。


可为空的值类型

在值类型的变量中,大部分值是不允许为空的,因此我们可以使用Nullable<T>T?定义可为空的值类型。但基础值类型 T 本身不能是可为空的值类型。

需要表示基础值类型的未定义值时,通常使用可为空的值类型。 例如,布尔值或 bool 变量只能为 true 或 false。 但是,在某些应用程序中,变量值可能未定义或缺失。 例如,某个数据库字段可能包含 true 或 false,或者它可能不包含任何值,即 NULL。 在这种情况下,可以使用 bool? 类型。

也就是说,当我们需要一个不可为空的值,而实际情况下可能会出现为空值的情况,我们就需要用到T?

由于值类型可隐式转换为相应的可为空的值类型,因此可以像向其基础值类型赋值一样,向可为空值类型的变量赋值。 还可分配 null 值。 例如:

double? pi = 3.14;
char? letter = 'a';

int m2 = 10;
int? m = m2;

bool? flag = null;

// An array of a nullable value type:
int?[] arr = new int?[10];

可为空值类型的默认值表示 null,也就是说,它是其 Nullable<T>.HasValue 属性返回 false 的实例。

通常判断可为空值内是否为空有三种做法:

int? a = 42;
if (a is int valueOfA) // valueOfA代表A的ASCII码对应值
{
}
if (a is null)
{
}
或者
if (a.HasValue)
{
}
或者
if (a != null)
{
}

从可为空的值类型转换为基础类型

如果要将可为空值类型的值分配给不可以为 null 的值类型变量,则可能需要指定要分配的替代 null 的值。

int? a = 28;
-- 使用??操作符,使用方法是a = x ?? y 或x ??= y
-- a = x??y当x为空,则a=y ,x非空则a= x
-- x??= y当x为空则x=y,非空则不处理
int b = a ?? -1;
Console.WriteLine($"b is {b}");  // output: b is 28

int? c = null;
int d = c ?? -1;
Console.WriteLine($"d is {d}");  // output: d is -1

注意,实际上TT?不是同一种值类型,所以同为值类型如果使用强制转换是可以的,但是如果把一个空值转换给一个非空类型是会报错的:

int? n = null;

//int m1 = n;    // Doesn't compile
int n2 = (int)n; // Compiles, but throws an exception if n is null

提升的运算符

任何T类型本身所支持的运算符,如果在运算时带有了T?类型,那么运算也是可以正常运行的。这些运算符将被提升,而运算结果将变为可为空值,但是类型还是需要符合T运算时的类型转换(例如int+float=浮点型,所以int?+folat?=浮点型?)。

int? a = 10;
float? b = null;
double? c = 0;

c = a + b;  // a is null
print(c); --Null

而bool值的计算稍微特殊,总体上也是符合bool运算法则的(我在lua学习笔记中总结了Lua入门):

bool? a = true;
bool? b = null;
bool? c = true;
c = a & b;
Debug.Log(c); --null
c = a | b;
Debug.Log(c); --true

对于比较运算符<、>、<= 和 >=,如果一个或全部两个操作数都为 null,则结果为 false;否则,将比较操作数的包含值。而带有null值时唯一可以进行比较运算的只有==!=

int? a = 10;
Console.WriteLine($"{a} >= null is {a >= null}");
Console.WriteLine($"{a} < null is {a < null}");
Console.WriteLine($"{a} == null is {a == null}");
// Output:
// 10 >= null is False
// 10 < null is False
// 10 == null is False

int? b = null;
int? c = null;
Console.WriteLine($"null >= null is {b >= c}");
Console.WriteLine($"null == null is {b == c}");
// Output:
// null >= null is False
// null == null is True

如何确定可为空的值类型

方法一:IsNullable(typeof(T?))

Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

// Output:
// int? is nullable value type
// int is non-nullable value type

在获取可为空的值类型的时候,注意只能使用typeof()不能使用GetType(),后者只能返回基类的类型:

int? a = 17;
Type typeOfA = a.GetType();
Console.WriteLine(typeOfA.FullName);
// Output:
// System.Int32

此外,is关键字无法判断 intint?,默认它们是同类型

int? a = 42;
if (a is int valueOfA)
{
   print(a); --结果打印 42
}

为什么建议少用T?

T?虽然可以避免值类型接受空值,但是我们应该尽量避免使用T?,这是因为这个类型实际上是对T类型的装箱和拆箱。当我们声明这个变量的时候,它会被编译器装箱,而当我们操作T?的时候编译器又会对它拆箱,实际上它像是一个拥有T和另一个变量Null的类。为了避免装箱拆箱操作对内存的影响,能不用尽量不用。

装箱和取消装箱

由于 T?已装箱,因此如果我们再对其装箱则会产生以下的情况判断:

  • 如果 HasValue 返回 false,则生成空引用。
  • 如果 HasValue 返回 true,则基础值类型 T 的对应值将装箱,而不对 Nullable<T> 的实例进行装箱。(也就是重新对T类型的对应值装箱一次)

可将值类型 T 的已装箱值取消装箱到相应的可为空值类型 T?,如以下示例所示:

int a = 41;
object aBoxed = a; 
int? aNullable = (int?)aBoxed; -- 把已装箱的a取消装箱并重新装箱为int?
Console.WriteLine($"Value of aNullable: {aNullable}");

object aNullableBoxed = aNullable;   -- HasValue=true,则基础类型int将重新被装箱
if (aNullableBoxed is int valueOfA)
{
    Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
}

int? b = null;
object aNullableBoxed = b;   -- HasValue=false,则生成空引用
if (aNullableBoxed == null)
{
    Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
}
// Output:
// Value of aNullable: 41
// aNullableBoxed is boxed int: 41
// aNullableBoxed is boxed int: 41

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/826866.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为Stable Diffusion web UI开发自己的插件实战

最近&#xff0c;Stable Diffusion AI绘画受到了广泛的关注和热捧。它的Web UI提供了了一系列强大的功能&#xff0c;其中特别值得一提的是对插件的支持&#xff0c;尤其是Controlnet插件的加持&#xff0c;让它的受欢迎程度不断攀升。那么&#xff0c;如果你有出色的创意&…

【Linux命令200例】touch用来创建新的文件或者修改已有文件

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加上鲜…

Linux下.py文件只读问题以及解决过程

一、问题描述 如图&#xff0c;在Ubuntu Linux系统中使用pycharm管理项目文件时&#xff0c;无法编辑&#xff0c;提示文件为只读&#xff1a; 点击"OK"后仍旧无法清除只读模式&#xff0c;并报错&#xff1a; 二、问题解决 将问题定性为文件权限相关问题&#…

谁说配置难?这篇文章让你轻松掌握xilinx 7系列FPGA配置技巧

本文旨在通过讲解不同模式的原理图连接方式&#xff0c;进而配置用到引脚的含义(手册上相关引脚含义有四、五页&#xff0c;通过本文理解基本上能够记住所有引脚含义以及使用场景)&#xff0c;熟悉xilinx 7系列配置流程&#xff0c;以及设计原理图时需要注意的一些事项&#xf…

什么是SaaS?国内做得好的saas平台有哪些啊?

SaaS是Software as a Service的缩写&#xff0c;意为软件即服务。 它是指将软件应用程序部署在云计算服务器上&#xff0c;通过网络提供给用户的一种模式。 这个模式下&#xff0c;用户无需花费大量的资金购买和维护软件应用程序&#xff0c;只需要按需订阅使用。SaaS通常以订…

blender 毛发粒子

新建平面&#xff0c;点击右侧粒子系统&#xff0c;选择毛发&#xff0c;调整毛发长度&#xff0c;数量&#xff08;Number&#xff09;&#xff0c;调整数量是为了避免电脑卡顿&#xff1b; 上面设置的每一根柱子都可以变成一个物体&#xff0c;点击渲染&#xff0c;渲染为选…

Flink之JDBC Sink

这里介绍一下Flink Sink中jdbc sink的使用方法,以mysql为例 代码 import org.apache.flink.connector.jdbc.JdbcConnectionOptions; import org.apache.flink.connector.jdbc.JdbcExecutionOptions; import org.apache.flink.connector.jdbc.JdbcSink; import org.apache.fli…

027 - avg()函数

定义&#xff1a; AVG 函数返回数值列的平均值。NULL 值不包括在计算中。 语法&#xff1a; SELECT AVG(column_name) FROM table_name -- 实际操作&#xff08;计算salary的平均值&#xff09;&#xff1a; SELECT avg(salary) AS "平均工资" FROM employee ; --…

300行代码实现简易Spring框架

源码地址 该简易Spring框架实现的功能有 容器启动包扫描单例、多例BeanBean的创建依赖注入Aware回调函数初始化后处理器AOP 目录结构如下&#xff0c;service包为模拟业务逻辑&#xff0c;spring包为spring的实现&#xff08;核心&#xff09;&#xff0c;其中ApplicationCo…

MySQL数据库中.frm和.myi和.myd和.ibd文件是什么文件?

mysql 数据库 存储引擎是myisam, 在data目录下会看到3类文件&#xff1a;.frm、.myi、.myd &#xff08;1&#xff09;.frm–表定义&#xff0c;是描述表结构的文件。 &#xff08;2&#xff09;.MYD–"D"数据信息文件&#xff0c;是表的数据文件。 &#xff08;3&am…

mfc程序发布时带上必要的dll

mfc在开发机器上&#xff0c;运行时没问题的&#xff0c; 但如果到其他windows机器运行会报错&#xff0c;提示几个dll库文件找不到 如何处理&#xff1f; 不要慌&#xff0c;问题不大&#xff0c;不要盲目去下载dll&#xff0c;或到c盘windows下找&#xff0c; 完全没必要…

浅析大数据时代下的视频技术发展趋势以及AI加持下视频场景应用

视频技术的发展可以追溯到19世纪初期的早期实验。到20世纪初期&#xff0c;电视技术的发明和普及促进了视频技术的进一步发展。 1&#xff09;数字化&#xff1a;数字化技术的发明和发展使得视频技术更加先进。数字电视信号具有更高的清晰度和更大的带宽&#xff0c;可以更快地…

WebServer项目

web服务器是IO密集型的任务&#xff1a;> CPU个数 有限状态机来更高效地处理状态的转移&#xff1a; 【差不多捋顺&#xff0c;按模块自己写出文字讲解&#xff0c;讲出优化思路优化的效果&#xff0c;瓶颈是啥解决策略是啥】 【redis 如何实现】【innodb底层如何实现】【e…

物理分代垃圾回收器

内存结构 内存分配 堆上分配 大多数情况在eden【年轻代中的一个区域】上分配&#xff0c;偶尔会直接在old【老年代】上分配&#xff0c;细节取决于GC的实现。栈上分配&#xff08;发生了指针逃逸&#xff0c;又叫指针逃逸分析——JVM优化&#xff09; 原子类型的局部变量。 G…

【Linux命令200例】tee将输入内容输出到屏幕和文件

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加上鲜…

Dockerfile构建nginx镜像(编译安装)

Dockerfile构建nginx镜像 1、建立工作目录 [rootdocker ~]# mkdir nginx [rootdocker ~]# cd nginx/ 2、编写Dockerfile文件 [rootdocker nginx]# vim run.sh [rootdocker nginx]# vim Dockerfile #基于的基础镜像 FROM centos:7#镜像作者信息 MAINTAINER Crushlinux <…

【iOS】json数据解析以及简单的网络数据请求

文章目录 前言一、json数据解析二、简单的网络数据请求三、实现访问API得到网络数据总结 前言 近期写完了暑假最后一个任务——天气预报&#xff0c;在里面用到了简单的网络数据请求以及json数据的解析&#xff0c;特此记录博客总结 一、json数据解析 JSON是一种轻量级的数据…

《Spring Boot源码解读与原理分析》书籍推荐

Spring Boot是目前Java EE开发中颇受欢迎的框架之一。依托于底层Spring Framework的基础支撑&#xff0c;以及完善强大的特性设计&#xff0c;Spring Boot已成为业界流行的应用和微服务开发基础框架。 《Spring Boot源码解读与原理分析》共14章&#xff0c;分为4个部分。第一部…

css white-space属性

先看不换行的效果&#xff0c;调用.space类 再来看使用 white-space:nowrap的效果 运行结果如下&#xff1a;