如何在 C# 中使用索引设置结构的特定成员
本文关键字:结构 设置 成员 索引 | 更新日期: 2023-09-27 18:34:19
假设我有一个结构:
struct Vector
{
public int X, Y;
// ...
// some other stuff
}
和一个类:
class Map
{
public Vector this[int i]
{
get
{
return elements[i];
}
set
{
elements[i] = value;
}
}
private Vector[] elements
// ...
// some other stuff
}
我希望能够执行以下操作:map[index].X = 0;
但我不能,因为返回值不是变量。
如果可能的话,我该怎么做?
你应该避免可变结构。
如果您希望类型可变,请改用类。
class Vector
{
public int X { get; set; } // Use public properties instead of public fields!
public int Y { get; set; }
// ...
// some other stuff
}
如果要使用结构,请使其不可变:
struct Vector
{
private readonly int x; // Immutable types should have readonly fields.
private readonly int y;
public int X { get { return x; }} // No setter.
public int Y { get { return y; }}
// ...
// some other stuff
}
编译器阻止您执行此操作,因为索引器返回的对象副本而不是引用(结构是按值传递的(。索引器返回一个副本,你修改此副本,但你根本看不到任何结果。编译器可帮助您避免这种情况。
如果你想处理这种情况,你应该使用类来代替,或者改变你处理Vector的方式。你不应该修改它的值,而是在构造函数中初始化它的值,更多关于这个主题:为什么可变结构是"邪恶的"?
- 将向量定义为类,或
-
将值存储在临时变量中
var v = map[index];v.X = 0;地图[索引] = v;
或
- 添加要更改的功能地图[索引] = 地图[索引]。偏移量((
或
-
让 [] 运算符返回一个 setter 类
class Setter { Vector[] Data; int Index; public double X { get { return Data[Index]; } set { Data[Index] = new Vector(value, Data[Index].Y);}}}
公共二传手这个[int i] { 获取 { 返回新的 Setter(( { Data = elements, Index= i }; } }
尽管泛型类在许多方面都运行良好,但它们不提供任何通过引用访问结构的合理方法。 这是不幸的,因为在许多情况下,结构集合将提供比类对象集合更好的性能(减少内存占用和改进缓存局部性(和更清晰的语义。 使用结构数组时,可以使用像 ArrayOfRectangle[5].Width += 3;
这样的语句,效果非常明显:它将更新ArrayOfRectangle[5]
的字段X
,但不会影响任何其他类型为 Rectangle
的存储位置的字段X
。 唯一需要知道的是,ArrayOfRectangle
是一个Rectangle[]
,而Rectangle
是一个具有公共int
场X
的结构。 如果Rectangle
是一个类,并且ArrayOfRectangle[5]
中保存的实例曾经暴露给外界,则可能很难或不可能确定ArrayOfRectangle[5]
引用的实例是否也由其他代码持有,这些代码期望其实例的字段X
不会更改。 使用结构时可以避免此类问题。
考虑到 .net 集合的实现方式,最好的办法通常是复制结构,修改它,然后将其存储回去。 这样做有点令人讨厌,但对于不太大的结构,通过使用值类型实现的改进的内存占用和缓存局部性可能超过从数据结构显式复制对象和将数据结构显式复制到数据结构的额外代码。 与使用不可变类类型相比,这几乎肯定是一个重大胜利。
顺便说一下,我希望看到的是集合公开以下方法: OperateOnElement<paramType>(int index, ref T element, ref paramType param, ActionByRef<T,paramType> proc)
将使用集合的相应元素以及传入的参数调用proc
。 在许多情况下,无需创建闭包即可调用此类例程;如果这样的模式是标准化的,编译器甚至可以使用它来很好地自动生成字段更新代码。