简体   繁体   English

如何使用C#中的索引设置结构的特定成员

[英]How to set a specific member of a struct, using indexing in C#

Assuming I have a struct: 假设我有一个结构:

struct Vector
{
    public int X, Y;

    // ...
    // some other stuff
}

and a class: 和一个班级:

class Map
{
    public Vector this[int i]
    {
        get
        {
            return elements[i];
        }
        set
        {
            elements[i] = value;
        }
    }

    private Vector[] elements

    // ...
    // some other stuff
}

I want to be able to do something like: map[index].X = 0; 我希望能够做类似的事情: map[index].X = 0; but I can't, because the return value is not a variable. 但我不能,因为返回值不是变量。

How do I do this, if at all possible? 如果可能的话,我该怎么做?

You should avoid mutable structs. 你应该避免可变的结构。

If you want your type to be mutable use a class instead. 如果您希望您的类型是可变的,请使用类。

class Vector
{
    public int X { get; set; } // Use public properties instead of public fields!
    public int Y { get; set; }

    // ...
    // some other stuff
}

If you want to use a struct, make it immutable: 如果要使用结构,请将其设为不可变:

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
}

The compiler prevents you from doing this because the indexer returns a copy of an object not a reference (struct is passed by value). 编译器阻止您执行此操作,因为索引器返回对象的副本而不是引用(struct按值传递)。 The indexer returns a copy, you modify this copy and you simply don't see any result. 索引器返回一个副本,您修改此副本并且您根本看不到任何结果。 The compiler helps you avoid this situation. 编译器可以帮助您避免这种情况。

If you want to handle such situation you should use class instead or change the way you deal with Vector. 如果你想处理这种情况,你应该使用class或改变你处理Vector的方式。 You shouldn't modify it's value but initialize it's values in constructor, more on this topic: Why are mutable structs “evil”? 你不应该修改它的值,而是在构造函数中初始化它的值,更多关于这个主题: 为什么可变结构“邪恶”? .

  • define Vector as class, or 将Vector定义为类,或
  • store value in a temporary variable 将值存储在临时变量中

    var v = map[index]; var v = map [index]; vX = 0; vX = 0; map[index] = v; map [index] = v;

or 要么

  • add function to change map[index] = map[index].Offset() 添加函数来改变map [index] = map [index] .Offset()

or 要么

  • let the [] operator return a setter class 让[]运算符返回一个setter类

    class Setter { Vector[] Data; class Setter {Vector [] Data; int Index; int索引; public double X { get { return Data[Index]; public double X {get {return Data [Index]; } set { Data[Index] = new Vector(value, Data[Index].Y); } set {Data [Index] = new Vector(value,Data [Index] .Y); }}} }}}

    public Setter this[int i] { get { return new Setter() { Data = elements, Index= i }; public Setter this [int i] {get {return new Setter(){Data = elements,Index = i}; } } }}

Although generic classes work pretty well for many purposes, they do not provide any reasonable way to access structs by reference. 虽然泛型类在很多方面都能很好地工作,但是它们没有提供任何合理的方法来通过引用访问结构。 This is unfortunate since in many cases a collection of structs would offer better performance (both reduced memory footprint and improved cache locality) and clearer semantics than a collection of class objects. 这是不幸的,因为在许多情况下,结构集合将提供比类对象集合更好的性能(减少内存占用和改进的缓存局部性)和更清晰的语义。 When using arrays of structs, one can use a statement like ArrayOfRectangle[5].Width += 3; 使用结构数组时,可以使用类似ArrayOfRectangle[5].Width += 3;的语句ArrayOfRectangle[5].Width += 3; with very clear effect: it will update field X of ArrayOfRectangle[5] but it will not affect field X of any other storage location of type Rectangle . 具有非常明显的效果:它将更新ArrayOfRectangle[5]字段X ,但它不会影响Rectangle类型的任何其他存储位置的字段X The only things one needs to know to be certain of that are that ArrayOfRectangle is a Rectangle[] , and Rectangle is a struct with a public int field X . 需要知道的唯一事情是ArrayOfRectangle是一个Rectangle[] ,而Rectangle是一个带有公共int字段X If Rectangle were a class, and the instance held in ArrayOfRectangle[5] had ever been exposed to the outside world, could be difficult or impossible to determine whether the instance referred to by ArrayOfRectangle[5] was also held by some other code which was expecting that field X of its instance wouldn't change. 如果Rectangle是一个类,并且ArrayOfRectangle[5]ArrayOfRectangle[5]的实例曾经暴露给外部世界,那么很难或不可能确定ArrayOfRectangle[5]引用的实例是否也被其他一些代码所持有。期望它的实例的字段X不会改变。 Such problems are avoided when using structures. 使用结构时可以避免这些问题。

Given the way .net's collections are implemented, the best one can do is usually to make a copy of a struct, modify it, and store it back. 鉴于.net的集合实现的方式,最好的方法通常是制作结构的副本,修改它并将其存储回来。 Doing that is somewhat icky, but for structs that aren't too big, the improved memory footprint and cache locality achieved by using value types may outweigh the extra code to explicitly copy objects from and to the data structures. 这样做有点蠢,但是对于不太大的结构,通过使用值类型实现的改进的内存占用和缓存局部性可能超过将数据结构显式复制到数据结构的额外代码。 It will almost certainly be a major win compared with using immutable class types. 与使用不可变类类型相比,它几乎肯定是一个重大胜利。

Incidentally, what I'd like to see would be for collections to expose methods like: OperateOnElement<paramType>(int index, ref T element, ref paramType param, ActionByRef<T,paramType> proc) which would call proc with the appropriate element of the collection along with the passed-in parameter. 顺便说一句,我想看到的是集合暴露方法,如: OperateOnElement<paramType>(int index, ref T element, ref paramType param, ActionByRef<T,paramType> proc) ,它们将使用适当的元素调用proc集合以及传入的参数。 Such routines could in many cases be called without having to create closures; 在许多情况下,可以在不必创建闭包的情况下调用这样的例程; if such a pattern were standardized, compilers could even use it to auto-generate field-update code nicely. 如果这样的模式是标准化的,编译器甚至可以使用它来很好地自动生成字段更新代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM