Indexers

H

Hoop

Hi,
I have a class that uses a number of lists for data storing.
Up to this point in testing they have been public, an example of one
of them,
public List<string> circuitNameList = new List<string>();

I want to make them private and acess them via properties. Looking
into this I have found that for this type
of data one has to use an indexer, so I did make one,

public string this [int index]
{
get
{
return circuitNameList[index - 1].ToString();
}

set
{
circuitNameList.Add(value);
}

}

Using the above I see no way to know which list I am writing too or
reading from.
I must be missing something here.
What is the correct way for using preoperties to access data
collections?
Thanks for any help
Jeff
 
J

Jon Skeet [C# MVP]

Hoop said:
I have a class that uses a number of lists for data storing.
Up to this point in testing they have been public, an example of one
of them,
public List<string> circuitNameList = new List<string>();

I want to make them private and acess them via properties.

That's a very good urge to have :)
Looking
into this I have found that for this type
of data one has to use an indexer, so I did make one,

No, you don't *have* to use an indexer. You can write a normal property
which returns the list:

public List<string> CircuitNames
{
get { return circuitNameList; }
set { circuitNameList = value; }
}
public string this [int index]
{
get
{
return circuitNameList[index - 1].ToString();
}

Why are you reducing the index by 1? Collections in .NET are almost all
0-based - making your indexer 1-based is non-idiomatic.

Also, you really don't need to call ToString() given that
circuitNameList[index-1] already returns a string.
set
{
circuitNameList.Add(value);
}
}

I'm not keen on that. Suppose I have an empty list - I shouldn't be
able to do:

foo[10] = "value";

because it's ended up in foo[0] or foo[1].

A setter should set, not just add. If you want to add, call Add :)
Using the above I see no way to know which list I am writing too or
reading from.

Indeed - you're effectively saying that the type has that index itself.
I must be missing something here.
What is the correct way for using preoperties to access data
collections?

It's unfortunately not terribly nice - there's no such thing as a named
indexer in C#. If you don't want to expose the list directly (as with
the properties shown near the top of this post) you can hide it via a
custom collection, but it gets a bit messy.
 
P

Peter Duniho

[...]
Using the above I see no way to know which list I am writing too or
reading from.
I must be missing something here.
What is the correct way for using preoperties to access data
collections?

A given class can have only one indexer. It's not an appropriate way to
index multiple collections within a class, for that reason and also
because the indexer for the class really should semantically treat the
class itself as a collection, not a holder of collections (unless, of
course, you're using the indexer to index which collection you want :) ).

If you want to encapsulate multiple collections in a single class without
exposing the collections, you'll need to create plain old methods to
access elements within the collections. Alternatively, you could create
helper classes, one for each collection your original class contains. The
original class could return instances of those helper classes via
properties, and the helper classes could each have an indexer
accomplishing what you want.

Whatever you wind up doing, it's probably a good idea to try to make sure
that the accessing of the collections is clear in code. Indexers are
useful, but they should only be used when the thing you've put an indexer
on is really something that is otherwise treatable as an indexable
object. An indexer on a class that has other members that have nothing to
do with the collection being indexed is likely to be confusing when used
in practice.

Pete
 
H

Hoop

Hoop said:
I have a class that uses a number of lists for data storing.
Up to this point in testing they have been public, an example of one
of them,
public List<string> circuitNameList = new List<string>();
I want to make them private and acess them via properties.

That's a very good urge to have :)
Looking
into this I have found that for this type
of data one has to use an indexer, so I did make one,

No, you don't *have* to use an indexer. You can write a normal property
which returns the list:

public List<string> CircuitNames
{
    get { return circuitNameList; }
    set { circuitNameList = value; }

}
public string this [int index]
        {
            get
            {
                return circuitNameList[index - 1].ToString();
            }

Why are you reducing the index by 1? Collections in .NET are almost all
0-based - making your indexer 1-based is non-idiomatic.

Also, you really don't need to call ToString() given that
circuitNameList[index-1] already returns a string.
            set
            {
                circuitNameList.Add(value);
            }
        }

I'm not keen on that. Suppose I have an empty list - I shouldn't be
able to do:

foo[10] = "value";

because it's ended up in foo[0] or foo[1].

A setter should set, not just add. If you want to add, call Add :)
Using the above I see no way to know which list I am writing too or
reading from.

Indeed - you're effectively saying that the type has that index itself.
I must be missing something here.
What is the correct way for using preoperties to access data
collections?

It's unfortunately not terribly nice - there's no such thing as a named
indexer in C#. If you don't want to expose the list directly (as with
the properties shown near the top of this post) you can hide it via a
custom collection, but it gets a bit messy.

Hi Jon,
Excellent example and explanation, this will work well.
Thanks
Jeff
 
J

Jon Skeet [C# MVP]

Peter Duniho said:
[...]
Using the above I see no way to know which list I am writing too or
reading from.
I must be missing something here.
What is the correct way for using preoperties to access data
collections?

A given class can have only one indexer.

Not quite true - you can overload indexers. For instance:

using System;

class Doubler
{
public int this[int x]
{
get { return x * 2; }
}

public string this[string x]
{
get { return x + x; }
}
}

class Test
{
static void Main()
{
Doubler d = new Doubler();
Console.WriteLine(d[10]);
Console.WriteLine(d["yo"]);
}
}

This isn't as daft as it sounds - DataRow, for example, allows you to
index by column number, column name, and DataColumn, each optionally
also specifying a version.
It's not an appropriate way to index multiple collections within a class

Agreed. That would indeed be very silly.
 
P

Peter Duniho

Not quite true - you can overload indexers.

Right, sorry. But I would only do that when I was actually indexing the
same thing. It would be really confusing otherwise.

And I didn't check, but I assume that the indexer carries the same
restriction that overloading in general does: you cannot overload where
the only difference is the return type. So you'd be forced into using a
different index type for each collection you wanted to index within a
single class.

Fortunately, that all means that the silliness of using multiple indexers
to index multiple collections within a single class is that much more
obvious. You have to contort yourself too much to miss the clues. :)

Pete
 
J

Jon Skeet [C# MVP]

Peter Duniho said:
Right, sorry. But I would only do that when I was actually indexing the
same thing. It would be really confusing otherwise.

And I didn't check, but I assume that the indexer carries the same
restriction that overloading in general does: you cannot overload where
the only difference is the return type. So you'd be forced into using a
different index type for each collection you wanted to index within a
single class.
Indeed.

Fortunately, that all means that the silliness of using multiple indexers
to index multiple collections within a single class is that much more
obvious. You have to contort yourself too much to miss the clues. :)

True. I do think it's a shame that we can't have named indexers though.
When you want to expose elements of multiple collections in a readonly
fashion, it can be a pain. Generics help this somewhat though.
 
H

Hoop

[...]
Using the above I see no way to know which list I am writing too or
reading from.
I must be missing something here.
What is the correct way for using preoperties to access data
collections?

A given class can have only one indexer.  It's not an appropriate way to 
index multiple collections within a class, for that reason and also  
because the indexer for the class really should semantically treat the  
class itself as a collection, not a holder of collections (unless, of  
course, you're using the indexer to index which collection you want :) ).

If you want to encapsulate multiple collections in a single class without  
exposing the collections, you'll need to create plain old methods to  
access elements within the collections.  Alternatively, you could create 
helper classes, one for each collection your original class contains.  The  
original class could return instances of those helper classes via  
properties, and the helper classes could each have an indexer  
accomplishing what you want.

Whatever you wind up doing, it's probably a good idea to try to make sure  
that the accessing of the collections is clear in code.  Indexers are  
useful, but they should only be used when the thing you've put an indexer  
on is really something that is otherwise treatable as an indexable  
object.  An indexer on a class that has other members that have nothing to  
do with the collection being indexed is likely to be confusing when used  
in practice.

Pete

Yes, I did more than one so an unamed indexer would not work here.
 
R

Rene

I realize that Jon already mentioned something like this in one of his
replies but here is an example of using an indexer that I belive would do
what you want... I didn't say I would personally do something like this, but
the solution below is possible :)

---------------------------------

public enum CollectionName
{
Uno,
Dos,
}

public string this[CollectionName colName, int index]
{
get
{
if (colName == CollectionName.Uno)
{
// Stuff for collection Uno goes here.
}
else if (colName == CollectionName.Dos)
{
// Stuff for collection Dosgoes here.
}
....
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top