7

Suppose I have an array or any other collection for that matter in class and a property which returns it like following:

public class Foo
{
    public IList<Bar> Bars{get;set;}
}

Now, may I write anything like this:

public Bar Bar[int index]
{
    get
    {
        //usual null and length check on Bars omitted for calarity
        return Bars[index];
    }
}
nawfal
  • 70,104
  • 56
  • 326
  • 368
TheVillageIdiot
  • 40,053
  • 20
  • 133
  • 188
  • 1
    The `Bars` property already supports the index, so I am a bit confused about what you are trying to accomplish. – Anthony Pegram Jul 20 '10 at 19:18
  • sure @Anthony `Foo f=new Foo(); f.Bars[0];` wow I think I should sleep now! – TheVillageIdiot Jul 20 '10 at 19:20
  • @AnthonyPegram You might not want to expose `Bars`. It might be an implementation detail that should not be part of `Foo`s public API. Furthermore, this exposes more than the index property of `Bars`. `Bars` may be highly stateful, and exposing it might cause a user to be able to violate the invariants that `Foo` promises. – Undreren Aug 31 '17 at 07:03
  • Though the other question https://stackoverflow.com/questions/3344620/easy-creation-of-properties-that-support-indexing-in-c-sharp was asked a few days later but has more views, I'm closing this one. – TheVillageIdiot Nov 07 '19 at 21:05
  • See https://github.com/dotnet/csharplang/issues/471 for the request to add this functionality and all arguments for and against it. Until today, the developers refuse to add it because they don't see sufficient benefit for the language. – Tobias Knauss Mar 27 '20 at 21:29
  • Are those "named indexers", or "properties with arguments" ? – Larry Nov 13 '20 at 08:32

5 Answers5

15

No - you can't write named indexers in C#. As of C# 4 you can consume them for COM objects, but you can't write them.

As you've noticed, however, foo.Bars[index] will do what you want anyway... this answer was mostly for the sake of future readers.

To elaborate: exposing a Bars property of some type that has an indexer achieves what you want, but you should consider how to expose it:

  • Do you want callers to be able to replace the collection with a different collection? (If not, make it a read-only property.)
  • Do you want callers to be able to modify the collection? If so, how? Just replacing items, or adding/removing them? Do you need any control over that? The answers to those questions would determine what type you want to expose - potentially a read-only collection, or a custom collection with extra validation.
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Could you not copy the same style used here http://msdn.microsoft.com/en-us/library/146h6tk5.aspx and depending on how you implement the method get the same result? – Gage Jul 27 '10 at 17:53
  • @Gage: I'm not sure what you're saying here... that doesn't create a named indexer as far as I can see... – Jon Skeet Jul 27 '10 at 18:41
  • @JonSkeet Using `foo.Bars[index]`, as you suggest here, will expose `Bars` to a consumer of `Foo`. That means `Foo` literally cannot control any invariants with respect to `Bars`. Furthermore, all consumers of `Foo` are now strongly coupled to `Bars`. A `GetBar` and `SetBar` method would in this case be superior, at least in terms of information hiding. – Undreren Aug 31 '17 at 07:30
  • @Undreren: It wouldn't be as idiomatic, IMO. An alternative would be to expose `Bars` as a new type which has an indexer which can do whatever you want. That way the client still gets idiomatic access (`foo.Bars[i]`) but you get complete control over what that code does. – Jon Skeet Aug 31 '17 at 07:33
  • @JonSkeet Using a proxy class that only exposes an indexed property might be more idiomatic, but they are functionally equivalent. My gripe was more the strong coupling to `Bars`. Your answer made it seem (to me) like "the way to do it". In many (most?) cases, it blatantly violates OO design principles, such as "information hiding". – Undreren Aug 31 '17 at 07:37
  • 1
    @Undreren: It depends on whether you actually want the information to be hidden. You could also expose it as a `ReadOnlyCollection` - or the `IList` might already *be* a `ReadOnlyCollection`. Only a getter is needed for the sake of that access - whether there's also a setter depends on requirements. I prefer not to be overly dogmatic on this front - sometimes it's simplest for all concerned to expose a setter as well as a getter; sometimes a getter that still allows collection manipulation; sometimes read-only access. – Jon Skeet Aug 31 '17 at 08:14
  • 1
    @Undreren: I'll edit the answer to mention that, but fundamentally the question is about whether there are named indexers in C#, to which the answer is no. – Jon Skeet Aug 31 '17 at 08:15
1

You can roll your own "named indexer" however. See

Community
  • 1
  • 1
cdiggins
  • 17,602
  • 7
  • 105
  • 102
1

You can use explicitly implemented interfaces, as shown here: Named indexed property in C#? (see the second way shown in that reply)

Community
  • 1
  • 1
George Birbilis
  • 2,782
  • 2
  • 33
  • 35
1
public class NamedIndexProp
{
    private MainClass _Owner;
    public NamedIndexProp(MainClass Owner) { _Owner = Owner;
    public DataType this[IndexType ndx]
    {
        get { return _Owner.Getter(ndx); }
        set { _Owner.Setter(ndx, value); }
    }
}
public MainClass
{
    private NamedIndexProp _PropName;
    public MainClass()
    {
       _PropName = new NamedIndexProp(this);
    }
    public NamedIndexProp PropName { get { return _PropName; } }
    internal DataType getter(IndexType ndx)
    {
        return ...
    }
    internal void Setter(IndexType ndx, DataType value)
    {
       ... = value;
    }
}
Doug
  • 11
  • 2
0

Depending on what you're really looking for, it might already be done for you. If you're trying to use an indexer on the Bars collection, it's already done for you::

Foo myFoo = new Foo();
Bar myBar = myFoo.Bars[1];

Or if you're trying to get the following functionality:

Foo myFoo = new Foo();
Bar myBar = myFoo[1];

Then:

public Bar this[int index]
{
    get { return Bars[index]; }
}
TheVillageIdiot
  • 40,053
  • 20
  • 133
  • 188
Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
  • 5
    I think 'this' is not named property. – Denis535 Jun 30 '17 at 20:45
  • 2
    @wishmaster35 It isn't. I don't know why this answer was accepted, because it's a design smell: It strongly couples a consumer of `Foo` to `Bars`. In some cases this might not matter, but now `Foo` has no control over its own private data; everyone can modify `Bars` directly, around `Foo`. – Undreren Aug 31 '17 at 07:32