tags:

views:

181

answers:

3

I'm pretty darn new to C# and I can't figure out how to express something pretty simple.

I have a 3D array that is private.

I have no problem with the function that exposes the contents to read:

public Terrain Tile(int x, int y, int z) { return ....

but I also want an internal function that provides read/write access with a coordinate transformation.

There seems to be no way to specify a setter.

Looking at Microsoft's site it appears that it wants []'s instead of ()'s but that results in the compiler thinking it's an array definition and of course it barfs all over the place. Googling elsewhere I find plenty of people trying to modify a field of something returning a reference type which of course fails but this array is full of enums, not reference types.

Of course I can write a SetTile(x, y, z, terrain) function but being able to access it as an array is so much more clear & elegant, yet it appears to be impossible.

+4  A: 

You can define a 'view' class with an indexer which is basically a property with arguments:

private Terrain[,,] rawArray = ...;
private View transformedArray = new View(rawArray);

private class View
{
    private Terrain[,,] array;

    public View(Terrain[,,] array)
    {
        this.array = array;
    }

    public Terrain this[int x, int y, int z]
    {
        get { ... }
        set
        {
            this.array[2*x, 3*z, -y] = value;
        }
    }
}
dtb
Same problem as with silky's answer--I want both the raw view and the transformed view.
Loren Pechtel
Hey dtb, can I get an opinion on my class?
Yuriy Faktorovich
I do agree that indexers are what I want (why the separate term??) but I'm not following what you are doing here. This provides the transformed array but I don't see how it's also doing the raw one.
Loren Pechtel
You'd create a different View typed class for each transform, which is why I wrote a base class which allows you to just insert a lambda function for the transform. But for the raw one you could just simply put an indexer in the same class as the Terrain[,,].
Yuriy Faktorovich
@Loren: That's why I kept the `rawArray` at the top level, so you can access both the raw array and the `View` instance. Of course, you can define additional `View` classes for different transformations or use lambdas. (If this is not what you want, please explain "raw view".)
dtb
I think I've got it. I'll try it.
Loren Pechtel
A: 

To extend on dtb's answer, I wrote the following transform class:

public class Transform<T, K>
{
    Func<K, T> _getFunc1;
    Func<K, K, T> _getFunc2;
    Func<K, K, K, T> _getFunc3;
    Action<K, T> _setFunc1;
    Action<K, K, T> _setFunc2;
    Action<K, K, K, T> _setFunc3;
    public T this[K k1]
    {
        get 
        {
            if (_getFunc1 == null) throw new ArgumentException();
            return _getFunc1(k1);
        }
        set 
        {
            if (_getFunc1 == null) throw new ArgumentException();
            _setFunc1(k1, value);
        }
    }

    public T this[K k1, K k2]
    {
        get
        {
            if (_getFunc2 == null) throw new ArgumentException();
            return _getFunc2(k1, k2);
        }
        set
        {
            if (_getFunc2 == null) throw new ArgumentException();
            _setFunc2(k1, k2, value);
        }
    }

    public T this[K k1, K k2, K k3]
    {
        get
        {
            if (_getFunc3 == null) throw new ArgumentException();
            return _getFunc3(k1, k2, k3);
        }
        set
        {
            if (_getFunc3 == null) throw new ArgumentException();
            _setFunc3(k1, k2, k3, value);
        }
    }

    public Transform(Func<K, T> getFunc) { this._getFunc1 = getFunc; }
    public Transform(Func<K, T> getFunc, Action<K, T> setFunc) 
        : this(getFunc)
    {
        this._setFunc1 = setFunc;
    }
    public Transform(Func<K, K, T> getFunc) { this._getFunc2 = getFunc; }
    public Transform(Func<K, K, T> getFunc, Action<K, K, T> setFunc)
        : this(getFunc)
    {
        this._setFunc2 = setFunc;
    }
    public Transform(Func<K, K, K, T> getFunc) { this._getFunc3 = getFunc; }
    public Transform(Func<K, K, K, T> getFunc, Action<K, K, K, T> setFunc)
        : this(getFunc)
    {
        this._setFunc3 = setFunc;
    }
}

Allowing you to create sample classes like the following:

class TransformUser
{
    int[, ,] _array = new int[4, 4, 4];

    public Transform<int, int> Normal;
    public Transform<int, int> Transformed;

    public TransformUser()
    {
        for (int i = 0; i < 4; i++)
            for (int j = 0; j < 4; j++)
                for (int k = 0; k < 4; k++)
                    _array[i, j, k] = i * j * k;

        Normal = new Transform<int, int>((x, y, z) => _array[x, y, z]);
        Transformed = new Transform<int, int>((x, y, z) => _array[x, y / 2, z]);
    }
}

With a use like the following:

TransformUser tu = new TransformUser();
Console.WriteLine(tu.Normal[2, 3, 2]);
Console.WriteLine(tu. Transformed[2, 3, 2]);
Yuriy Faktorovich
A: 

Here's one option:

private Terrain[,,] rawArray = ...;
private View view = new View(rawArray);


private class View
{
    private class TransformedView
    {
        private Terrain[,,] array;
        public TransformedView(Terrain[,,] array) 
        {
            this.array = array;
        }

        public Terrain this[int x, int y, int z]
        {
            get { ... }
            set
            {
                this.array[2*x, 3*z, -y] = value;
            }
        }
    }

    private Terrain[,,] array;
    public readonly TransformedView Transformed;
    public View(Terrain[,,] array)
    {
        this.array = array;
        Transformed = new TransformedView(array);
    }

    public Terrain this[int x, int y, int z]
    {
        get { ... }
        set
        {
            this.array[x, z, y] = value;
        }
    }
}
Patrick