简体   繁体   中英

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; 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). 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. 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
  • store value in a temporary variable

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

or

  • add function to change map[index] = map[index].Offset()

or

  • let the [] operator return a setter class

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

    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; 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 . 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 . 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. 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. 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. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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