将(通用)值类型强制转换为引用类型

8

我有一个具有一些字段的结构体。其中一个字段是通用类型。通用类型可以是引用类型或值类型

我想强制将其作为内部引用存储,以避免结构体过大。

struct Foo<T>
{
  T field; // should be a reference
}

我知道我可以使用objectT[],但两者都很笨重。难道没有类似于通用引用类型的东西吗?

struct Foo<T>
{
  Reference<T> field;
}

是的,当然,我可以自己编写。但我试图避免那样做。


我不明白那如何解决问题。假设S是一个大的值类型。那么Foo<S>与S的大小相同。如果用户对S的性能满意,那么为什么他们对Foo<S>的性能也不满意呢?无论您使用对象还是自定义的装箱类来装箱S,它都会使用更多的空间,因为您必须为S的实例和盒子留出空间。装箱S将使用更多时间,因为装箱和取消装箱S需要时间。 - Eric Lippert
将S保留在引用类型中,如果存在大量的S,则会给垃圾收集器增加相当大的压力。您能解释一下为什么您认为这是任何胜利的方式吗?因为对我来说,这似乎使一个糟糕的情况——即大型结构的性能成本——变得更糟。 - Eric Lippert
@Eric:我不希望这个结构体变得太大。结构体在内存使用上应该尽可能小。这个结构体中还有其他字段,而且已经接近上限了。如果T是Guid或者更大的值类型,那么结果会使结构体变得太大。 - Stefan Steinegger
好的,那么为什么Foo一开始是一个结构体呢?将Foo变成一个类。问题解决了。或者没有。你还没有说出问题是什么,所以我们只能猜测。你不能不付出相应的代价而获得任何收益;你可以通过使用结构体来交换更多的复制时间,或者通过使用类来增加集合压力,但是避免这两种情况的唯一方法就是在程序中减少数据量。你需要避免的成本是复制成本还是集合成本? - Eric Lippert
@Eric:我想让Foo成为一个结构体(出于各种原因,其中之一是避免null),T成为一个引用,因为Foo的总大小。始终如此。即使T是值类型。称其为“显式装箱”。类型安全性很好(接受的答案不是)。就是这样。现在清楚了吗? - Stefan Steinegger
@StefanSteinegger,你还没有解释为什么Foo可能是一个大结构体会成为问题。它是否在许多地方复制?它是否被传递(因此可能被复制)多次?还是其他原因?换句话说,你说foo会变得太大了。 "太"大是指什么?多大才算是“太”大? - Suzanne Soy
4个回答

11

定义一个类T。

struct Foo<T> where T : class
{
  T field; // Now it's a reference type.
}

不,我不想强制 T 是一个类。当 T 是值类型时,我希望它被存储为引用。 - Stefan Steinegger
2
你可以简单地将 field 包装成一个对象类型 [这是我想到的唯一好的解决方案]。你究竟想要做什么? - this. __curious_geek
我不想让这个结构变得太大。 结构应该具有较小的内存使用量。 在此结构中还有其他字段,并且已经达到了上限。 如果T是Guid或更大的值类型,则生成的结构将太大。 - Stefan Steinegger

3
您可以使用Tuple<T1>来保存您的值类型变量(Tuples是BCL中的类)。
struct Foo<T>
{
    Tuple<T> field;
}

2

如果你想确保任何值类型都被装箱,把它存储在对象字段中,并使用属性来强制泛型约束,例如:

struct Example<T>
{
    private object obj;
    public T Obj
    {
        get
        {
            return (T)obj;
        }
        set
        {
            this.obj = value;
        }
    }
}

需要注意的是,根据您使用该结构的方式,即使两个相同的值,它们的结构体的原始内存也可能不同,因为结构体并没有被interned。而且,装箱后将具有不同的指针。对于托管比较,Equals方法仍将工作,因此这仅适用于特定的托管->本机转换。 - TheXenocide
我曾认为元组是值类型,但显然它不是,所以我建议采用thecoop的答案。 - TheXenocide

-1

如果您希望它成为一个实例:

where T : new()

1
如果类型 T 的对象需要在运行时通过类型 T 的默认构造函数实例化,则始终为实例或 null 引用。您可以使用 new() 进行实例化。 - this. __curious_geek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接