c语言中的legal(?)可变结构体大小写#

legal(?) mutable struct case in c#

提问人:Feri 提问时间:1/24/2017 更新时间:2/1/2017 访问量:165

问:

我读过很多关于 c# 中结构体可变性的文章。大多数词都说可变结构是邪恶的。我明白为什么了。 但是我有一个可变结构似乎是合法的情况。当然,我面临着围绕结构可变性的问题。

比如说,我有一个简单的结构,在GIS编辑器应用程序中是这样:

enum CoordinateSystem : byte { .... } // coordinate system types

struct Vertex 
{
    float m_x; // m_ is the original value of coordinate, it needs to be kept
    float m_y;  
    float c_x; // c_ is a cache of the result of expensive coordinate transformation
    float c_y;
    CoordinateSystem cachedSystem; // the current coordinate system type in c_x and c_y
    public float x { get {...} set {...}} // properties are for accessing values outside of struct
    public float y { get {...} set {...}} // and transforming m_x,m_y into c_x,c_y if necessary
}

属性用于从外部访问坐标。从外部单例类中,结构体可以知道访问 x 和 y 属性时需要什么坐标系。如有必要,get 属性将按需转换m_x和m_y,除了返回值外,它还将结果存储在 c_x 和 c_y 中,以便进一步访问。当然,缓存是必要的,因为坐标转换非常昂贵。使用缓存时,只有第一次访问会很慢,并且同一坐标系中的所有进一步访问都是“即时”的。

顶点位于 List 中,因此基本上长度为 10000 的顶点列表在 List 中被分配为 Vertex[10000]。

显然,问题在于从列表中访问(退出)顶点会返回存储在列表中的结构体副本<>因此转换后的缓存不起作用,因为它将结果存储在短期副本中,而不是存储在列表本身<>。

我不想将 Vertex 更改为类,因为它会(在 64 位运行时上)分配 10000*8 字节作为一个块的引用,在 10000 个块中分配 10000 * 17 字节,这大约是 + 1/3 内存和 10000 倍分配的块数。(我知道这是一个粗略的计算,因为对齐,...... 这是关于一个顶点列表,我有一万个顶点列表,所以上课会浪费很多内存。

我想对外部访问器保持转换透明,因为 Vertex 是多次引用的应用程序中的核心类型。另一个原因是只有顶点知道坐标系,所有其他部分都把 Vertex.x 和 Vertex.y 看作是“数字”,仅此而已。这样,x 和 y 属性应该会进行转换。

问题是:解决这个问题的正确方法是什么?

(请不要将这个问题视为基于意见。这不是其中之一。考虑到申请的目的,我对基于事实的答案感兴趣。

c# 结构 可变

评论

0赞 Carey Gregory 1/24/2017
您不能使用修改后的副本更新列表吗?
0赞 M.kazem Akhgary 1/24/2017
我认为最好将不可变部分和可变部分分开,为实际值和缓存值提供单独的类型。我不认为每个顶点都会有所不同。因此,最好将缓存的值放在另一个列表中,并在发生更改时使用新值重新创建列表。CoordinateSystemCoordinateSystem
1赞 Ivan Stoev 1/24/2017
这样的 s 仅在从数组存储/公开时才有用,数组是目前唯一允许通过索引器更改结构元素的集合结构。C#7 可能允许公开更多的索引器,如数组,但在那之前......此外,为了向后兼容,他们可能会保持现在的状态。structref returnsList<T>
0赞 Feri 2/1/2017
M.kazem Akhgary:“每当坐标系统更改时,都会重新创建列表”——不幸的是,这是不可能的,它需要 1) 重组应用程序的核心,2) 重新计算用户更改 Coo 系统时需要几十秒的时间,这是不可接受的;我的问题是关于 1) 透明 2) 按需(即访问缓存值)解决方案。
1赞 Enigmativity 2/1/2017
@M.kazemAkhgary - 您收到了 OP 的回复。

答:

0赞 Feri 2/1/2017 #1

正如 Ivan Stoyev 告诉我们的那样,“数组,这是目前唯一允许通过索引器改变结构元素的集合结构”。 而 List<> 使用数组。

所以我选择了一个肮脏的、应受谴责的、不堪称典范的和邪恶的解决方案,我预见到反对票......但我必须有一个适合内存中千兆字节数据的解决方案,并且不需要更改太多代码。

我修改了 List<> 类的索引器,以在访问列表元素时更改(重新计算)位于存储数组中的结构的缓存部分。

不幸的是,List<> 的存储数组是私有的,不受保护,无法通过继承访问它。因此,我以一种更恶魔的方式走得更远:从 referencesource 中,我复制了 List 源代码的必要部分<>并且(当然,使用不同的名称)最后我有一个具有 List 功能的新类<>但能够在访问索引器时更新内容。

同样,这是一个任何人都不应该遵循的解决方案。 但它效果很好......

评论

1赞 supercat 2/2/2017
我早就想过,并且应该包括一种方法,该方法可以调用 [可能,带有省略的形式]。这样的函数将使代码能够获得能够访问底层数组的许多好处,但不需要集合公开其数组(甚至使用一个数组)。List<T>IList<T>AccessElement<U>(int index, ActionByValueRefRef<int,T,U> proc, ref U extraValue)proc(index, ref listItem[index], ref extraValue)extraValue