Is it possible to extend the indexer

K

kndg

Hi all,

Suppose I have a Customer class defined below,

public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }

public Customer(int id, string name, string address)
{
Id = id;
Name = name;
Address = address;
}
}

and a list of customers,

var customers = new List<Customer>();
customers.Add(new Customer(1, "A", "A avenue"));
customers.Add(new Customer(2, "R", "R road"));
customers.Add(new Customer(3, "S", "S street"));

List is indexed using numbers, so if I want to access the list using
customer's name I have to resort using Dictionary<string, Customer>.
But, what if I want to access the list using both number and customer name?

I could just inherit a List and make a collection class like below,

public class CustomerCollection : List<Customer>
{
public Customer this[string customerName]
{
get
{
foreach (var item in this)
{
if (item.Name == customerName) return item;
}

return null;
}
}
}

var customers2 = new CustomerCollection();
customers2.AddRange(customers);

Console.WriteLine(customers[1].Address);
Console.WriteLine(customers["R"].Address);

But, I'm wondering if it possible to just extend the indexer like this,

internal static class MyExtension
{
public static Customer this[this List<Customer> list, string
customerName]
{
get
{
foreach (var item in list)
{
if (item.Name == customerName) return item;
}

return null;
}
}
}

Yeah, the above won't compile, but I think it would be cool.

Regards.
 
P

Peter Duniho

[...]
var customers = new List<Customer>();
customers.Add(new Customer(1, "A", "A avenue"));
customers.Add(new Customer(2, "R", "R road"));
customers.Add(new Customer(3, "S", "S street"));

List is indexed using numbers, so if I want to access the list using
customer's name I have to resort using Dictionary<string, Customer>.

You write that as if it's a bad thing.
But, what if I want to access the list using both number and customer
name?

It seems like there's already a bit of confusion here. You're right, you
can index the List<Customer> by index. But what index? You have an "id"
in your Customer, but that's not in any way tied reliably to the index in
the List<Customer>.

Basically, you've already identified the solution: use the appropriate
collection class for retrieval. If your retrieval pattern follows an
"indexed by insertion order", then a List<T> is just fine. But if you
want to retrieve your Customer instances based on some key field or
property, then you really do want a Dictionary<TKey, TValue>.

I suppose that one might be able to come up with a really compelling
reason to support indexers as part of the extension method feature. But I
don't think this is really such an example. An extension indexer in this
case would only serve to obfuscate what's actually happening, and
improperly encourage continued use of the wrong collection type for the
usage pattern.

Pete
 
K

kndg

Hi Pete

Peter said:
[...]
var customers = new List<Customer>();
customers.Add(new Customer(1, "A", "A avenue"));
customers.Add(new Customer(2, "R", "R road"));
customers.Add(new Customer(3, "S", "S street"));

List is indexed using numbers, so if I want to access the list using
customer's name I have to resort using Dictionary<string, Customer>.

You write that as if it's a bad thing.
No, not at all. Just my bad wording.
It seems like there's already a bit of confusion here. You're right,
you can index the List<Customer> by index. But what index? You have an
"id" in your Customer, but that's not in any way tied reliably to the
index in the List<Customer>.
Yes, the index is not tied to "Id". Sorry for the confusion.
Basically, you've already identified the solution: use the appropriate
collection class for retrieval. If your retrieval pattern follows an
"indexed by insertion order", then a List<T> is just fine. But if you
want to retrieve your Customer instances based on some key field or
property, then you really do want a Dictionary<TKey, TValue>.
I'm write this because I need the flexibility.
I come across my own old code and it have a lot of for-loop.
After doing some modification, it becomes clear that my list should
support retrieval by string. Instead modifying the whole code, I thought
it would be nice if I could just extend the indexer.
I suppose that one might be able to come up with a really compelling
reason to support indexers as part of the extension method feature. But
I don't think this is really such an example. An extension indexer in
this case would only serve to obfuscate what's actually happening, and
improperly encourage continued use of the wrong collection type for the
usage pattern.
Yes, agreed.

Thanks.
 
P

Peter Duniho

[...]
Basically, you've already identified the solution: use the appropriate
collection class for retrieval. If your retrieval pattern follows an
"indexed by insertion order", then a List<T> is just fine. But if you
want to retrieve your Customer instances based on some key field or
property, then you really do want a Dictionary<TKey, TValue>.
I'm write this because I need the flexibility.
I come across my own old code and it have a lot of for-loop.
After doing some modification, it becomes clear that my list should
support retrieval by string. Instead modifying the whole code, I thought
it would be nice if I could just extend the indexer.

True, it would be "nice" in the sense that it would be convenient. But
the potential long-term maintenance issues have a good chance of
outweighing that benefit.

For occasional use (i.e. not a performance bottleneck), I think a purely
explicit approach is probably best, even if a little more wordy:

string strName = "A";
Customer cust = customers.Find(c => c.Name == strName);

Just write the Find() call any time you actually need to do the lookup.

If you need to retrieve these things more often, in a
performance-sensitive context, then probably it's best to just keep the
items in a Dictionary<TKey, TValue> instead. And yes, that means
reworking the code. But it's probably better in the long run. You can
always encapsulate the lookup code in a method -- extension methods are
simply a syntactical convenience anyway -- so that the broader code
changes are minimized.

Writing a separate method also gives you the freedom to tailor the lookup
code according to your specific needs, allowing you to update it as those
needs change over time. So it's not that a specific method isn't a good
approach; it's just that the extension approach is unnecessary (you'd
still have to revisit each call site anyway, to use a string instead of an
index) and probably will make the code less clear.

Pete
 

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