Stu said:
I have a private array in a class, and I was just wondering if it's
possible to expose its elements with "array-like" syntax through a
property without exposing the full array, instead of using a method.
So for instance could I do something like this:
public class MyClass
{
private int[] _NumberArray = new int[100];
public int NumberArray[int index]
{
get
{
return _NumberArray[index];
}
set
{
_NumberArray[index] = value;
}
}
public MyClass(){}
}
Then call it like this so that when MyClass.NumberArray[index] gets
assigned a value, it does it through the property:
MyClass myClass = new MyClass();
myClass.NumberArray[index] = 5;
I know this is easily accomplished through a method, but then I have
to pass the index and value as parameters rather than using array-like
syntax. Not a big deal, more a matter of curiosity.
In C#, no. VB.NET has indexed properties, which are similar to a class
indexer in C#, except you can give it a property name. But in C#, the
only indexer you can declare is for the class itself.
In C#, the proper idiom is to define a property that itself is a type
with an indexer. That's assuming it makes sense to provide access to
the array in this way at all, which is not always true (people often
want to construct their class in counter-intuitive ways
).
What approach is appropriate depends on the relationship between the
array and the class containing it. If the array is just an
implementation detail of a class that is itself array-like, then simply
giving the class an indexer is probably best:
public class MyClass
{
private int[] _NumberArray = new int[100];
public int this[int index]
{
get
{
return _NumberArray[index];
}
set
{
_NumberArray[index] = value;
}
}
public MyClass(){}
}
Usage example:
MyClass obj = new MyClass();
obj[50] = 17;
Console.WriteLine(obj[25]);
On the other hand, often an array in a class is a contained object that
the class is responsible for maintaining. In those cases, either you
want to prevent modifications to the array or you don't (and if you're
providing a "set" method in an indexed property, obviously you don't
want to prevent modifications to the array).
If you don't want to prevent modifications to the array, then just
define a property that returns the array object. Code can then index
off of _that_ property. It will look just like an indexed property, but
will be using the "approved" C# mechanism of a plain named property
along with a returned class with its own indexer:
public class MyClass
{
private int[] _NumberArray = new int[100];
public int[] NumberArray
{
get
{
return _NumberArray;
}
}
public MyClass(){}
}
Usage example:
MyClass obj = new MyClass();
obj.NumberArray[50] = 17;
Console.WriteLine(obj.NumberArray[25]);
In other cases, it can be useful to do basically the above, but return
something other than the actual object type declared. In some cases,
that's just a matter of using an interface the object already implements
(e.g. ICollection), while in other cases it's a matter of wrapping the
object in something that provides greater control (e.g. preventing
modification of the object, by returning Array.AsReadOnly())
Finally, note that you can provide arbitrarily complex degree of control
over access by defining your own wrapper type that would be exposed by a
named property, and putting that control in that type.
For example:
public class MyClass
{
private int[] _NumberArray = new int[100];
public class Indexer
{
private MyClass _obj;
public Indexer(MyClass obj)
{
_obj = obj;
}
public int this[int i]
{
get { return _obj._NumberArray
; }
set { _obj._NumberArray = value; }
}
}
public Indexer NumberArray
{
get
{
return new Indexer(this);
}
}
public MyClass(){}
}
Usage example:
MyClass obj = new MyClass();
obj.NumberArray[50] = 17;
Console.WriteLine(obj.NumberArray[25]);
Obviously in a real-world example, you'd put whatever specific access
controls you want into the "Indexer" class. You can enhance
encapsulation by exposing only an interface, and keeping the
implementation private:
public class MyClass
{
private int[] _NumberArray = new int[100];
public interface IIndexer
{
public int this[int i] { get; set; }
}
private class Indexer : IIndexer
{
private MyClass _obj;
public Indexer(MyClass obj)
{
_obj = obj;
}
public int this[int i]
{
get { return _obj._NumberArray; }
set { _obj._NumberArray = value; }
}
}
public IIndexer NumberArray
{
get
{
return new Indexer(this);
}
}
public MyClass(){}
}
Usage example:
MyClass obj = new MyClass();
obj.NumberArray[50] = 17;
Console.WriteLine(obj.NumberArray[25]);
(Warning: all of the above code is uncompiled, untested, example code only).
Anyway, hopefully that gives you some ideas as to the best way to expose
your indexed data, whatever the exact context turns out to be.
Pete