C# 为什么为空<;T>;是一个结构吗?
我想知道为什么C# 为什么为空<;T>;是一个结构吗?,c#,clr,nullable,C#,Clr,Nullable,我想知道为什么Nullable是一个值类型,如果它被设计成模仿引用类型的行为的话?我理解GC压力之类的事情,但我不觉得有说服力——如果我们想让int像引用一样工作,我们可能可以接受使用真实引用类型的所有后果。我看不出为什么Nullable不仅仅是Tstruct的盒装版本 作为值类型: 它仍然需要装箱和取消装箱,而且装箱必须与“普通”结构有所不同(要像对待实null一样对待空值null) 在检查null时,需要对其进行不同的处理(只需在Equals中完成,没有实际问题) 它是可变的,打破了结构应该
Nullable
是一个值类型,如果它被设计成模仿引用类型的行为的话?我理解GC压力之类的事情,但我不觉得有说服力——如果我们想让int
像引用一样工作,我们可能可以接受使用真实引用类型的所有后果。我看不出为什么Nullable
不仅仅是T
struct的盒装版本
作为值类型:
null
一样对待空值null)Equals
中完成,没有实际问题)Nullable
设为可空
引用类型难道不能解决这个问题吗
重新措辞并更新:
我对我的理由列表做了一些修改,但我的一般问题仍然没有解决:
引用类型Nullable
如何比当前值类型实现更糟糕?这仅仅是GC压力和“小而不变”的规则吗?这对我来说仍然很奇怪…编辑以解决更新的问题
如果要将结构用作引用,可以装箱和取消装箱对象
但是,Nullable
类型基本上允许使用一个附加的状态标志来增强任何值类型,该状态标志指示该值是否应作为null
使用,或者该值是否“有效”
因此,为了回答您的问题:
实例。装箱一个“没有”值的null
将返回一个null
引用,而取消装箱则相反
where T:struct
约束泛型类型,则不允许使用可为空的类型。由于此约束也定义在Nullable
类型上,因此如果不进行任何特殊处理,就无法嵌套它们为什么不使用参考资料?我已经提到了重要的语义差异。但除此之外,引用类型使用了更多的内存空间:每个引用,特别是在64位环境中,不仅会占用实例的堆内存,还会占用引用的内存、实例类型信息、锁定位等。。因此,除了语义和性能差异(通过引用间接寻址)之外,对于大多数常见的实体,您最终将使用实体本身使用的内存的倍数。GC需要处理更多的对象,这将使总体性能比结构更差。它是不可变的;再查一遍 拳击也不一样;将空“框”设置为空
但是;它很小(略大于T),不可变,并且只封装结构——理想的结构。也许更重要的是,只要T是一个真正的“值”,那么T也是吗?逻辑“值”。原因是它不是设计成一个引用类型。它被设计成一个值类型,除了一个特定的类型。让我们看看值类型和引用类型的一些不同之处 值和引用类型之间的主要区别在于,值类型是自包含的(包含实际值的变量),而引用类型引用另一个值 这还带来了其他一些差异。事实上,我们可以直接别名引用类型(它有好的和坏的影响),这就是原因。平等的含义也存在差异: 值类型具有基于包含的值的相等概念,可以选择重新定义该值(对于如何进行重新定义存在逻辑限制*)。引用类型的标识概念对于无法重新定义的值类型没有意义(因为它们不能直接别名,所以两个这样的值不能相同),这也为其相等概念提供了默认值。默认情况下,
=
在值类型†上处理基于值的相等,但在引用类型上处理基于标识的相等。此外,即使引用类型被赋予了基于值的相等概念,并将其用于=
,它也不会失去与另一个标识引用进行比较的能力
由此产生的另一个区别是引用类型可以为null-引用另一个值的值允许不引用任何值的值,这就是null引用
此外,保持值类型较小的一些优点与此相关,因为它们基于值,在传递给函数时会按值复制
其他一些差异是隐含的,但不是由此产生的。使值类型不可变通常是一个好主意,这是隐含的,但不是核心差异所带来的,因为虽然在不考虑实现问题的情况下可以找到一些优势,但在引用类型中这样做也有一些优势(实际上,一些与别名安全相关的优势更直接地应用于引用类型)以及人们可能违反这一准则的原因——因此这不是一条硬性规定(对于嵌套值类型,所涉及的风险大大降低,因此即使我的风格非常倾向于mak,我也不会对使嵌套值类型可变感到不安)
struct Nullable<T>
{
private bool hasValue;
internal T value;
/* methods and properties I won't go into here */
}
namespace ClassLibrary1
using NUnit.Framework;
[TestFixture]
class MyNullableShould
{
[Test]
public void operator_equals_btw_nullable_and_value_works()
{
var myNullable = new MyNullable<int>(1);
Check.That(myNullable == 1).IsEqualTo(true);
Check.That(myNullable == 2).IsEqualTo(false);
}
[Test]
public void Can_be_comparedi_with_operator_equal_equals()
{
var myNullable = new MyNullable<int>(1);
var myNullable2 = new MyNullable<int>(1);
Check.That(myNullable == myNullable2).IsTrue();
Check.That(myNullable == myNullable2).IsTrue();
var myNullable3 = new MyNullable<int>(2);
Check.That(myNullable == myNullable3).IsFalse();
}
}
public class MyNullable<T> where T : struct
{
internal T value;
public MyNullable(T value)
{
this.value = value;
this.HasValue = true;
}
public bool HasValue { get; }
public T Value
{
get
{
if (!this.HasValue) throw new Exception("Cannot grab value when has no value");
return this.value;
}
}
public static explicit operator T(MyNullable<T> value)
{
return value.Value;
}
public static implicit operator MyNullable<T>(T value)
{
return new MyNullable<T>(value);
}
public static bool operator ==(MyNullable<T> n1, MyNullable<T> n2)
{
if (!n1.HasValue) return !n2.HasValue;
if (!n2.HasValue) return false;
return Equals(n1.value, n2.value);
}
public static bool operator !=(MyNullable<T> n1, MyNullable<T> n2)
{
return !(n1 == n2);
}
public override bool Equals(object other)
{
if (!this.HasValue) return other == null;
if (other == null) return false;
return this.value.Equals(other);
}
public override int GetHashCode()
{
return this.HasValue ? this.value.GetHashCode() : 0;
}
public T GetValueOrDefault()
{
return this.value;
}
public T GetValueOrDefault(T defaultValue)
{
return this.HasValue ? this.value : defaultValue;
}
public override string ToString()
{
return this.HasValue ? this.value.ToString() : string.Empty;
}
}