免费企业建站源代码,怎么上网站,重庆在线平台,网站建站建设公司文章目录 Struct结构体类型为什么不推荐struct 元组类型可为空的值类型从可为空的值类型转换为基础类型提升的运算符如何确定可为空的值类型为什么建议少用T?装箱和取消装箱 Struct结构体类型
结构类型#xff08;“structure type”或“struct type”#xff09;是一种可封… 文章目录 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.使用元组类型的情况通常用于接受函数多返回值。如果想要一个可变动的带有方法的数据结构类还是优于元组的。 可为空的值类型
在值类型的变量中大部分值是不允许为空的因此我们可以使用NullableT 或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也就是说它是其 NullableT.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为空则ay ,x非空则a x
-- x?? y当x为空则xy,非空则不处理
int b a ?? -1;
Console.WriteLine($b is {b}); // output: b is 28int? c null;
int d c ?? -1;
Console.WriteLine($d is {d}); // output: d is -1注意实际上T和T?不是同一种值类型所以同为值类型如果使用强制转换是可以的但是如果把一个空值转换给一个非空类型是会报错的
int? n null;//int m1 n; // Doesnt compile
int n2 (int)n; // Compiles, but throws an exception if n is null提升的运算符
任何T类型本身所支持的运算符如果在运算时带有了T?类型那么运算也是可以正常运行的。这些运算符将被提升而运算结果将变为可为空值但是类型还是需要符合T运算时的类型转换例如intfloat浮点型,所以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 Falseint? 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关键字无法判断 T 和T?默认它们是同类型
int? a 42;
if (a is int valueOfA)
{print(a); --结果打印 42
}为什么建议少用T?
T?虽然可以避免值类型接受空值但是我们应该尽量避免使用T?这是因为这个类型实际上是对T类型的装箱和拆箱。当我们声明这个变量的时候它会被编译器装箱为T?而当我们操作T?的时候编译器又会对它拆箱实际上它像是一个拥有T和另一个变量Null的类。为了避免装箱拆箱操作对内存的影响能不用尽量不用。
装箱和取消装箱
由于 T?已装箱因此如果我们再对其装箱则会产生以下的情况判断
如果 HasValue 返回 false则生成空引用。如果 HasValue 返回 true则基础值类型 T 的对应值将装箱而不对 NullableT 的实例进行装箱。(也就是重新对T类型的对应值装箱一次)
可将值类型 T 的已装箱值取消装箱到相应的可为空值类型 T?如以下示例所示
int a 41;
object aBoxed a;
int? aNullable (int?)aBoxed; -- 把已装箱的a取消装箱并重新装箱为int?
Console.WriteLine($Value of aNullable: {aNullable});object aNullableBoxed aNullable; -- HasValuetrue,则基础类型int将重新被装箱
if (aNullableBoxed is int valueOfA)
{Console.WriteLine($aNullableBoxed is boxed int: {valueOfA});
}int? b null;
object aNullableBoxed b; -- HasValuefalse,则生成空引用
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