Jump to content
Xtreme .Net Talk

Recommended Posts

Posted (edited)

Hiya all,

 

I'm not a newbie to .NET by any means, but I've obviously missed something big here.

 

For some reason I can't seem to get indexed properties to work! Well, not in the way I want (and think to be most logical), I can get them to work like:

public int this[int index]
{
   get { ... }
   set { ... }
}

but not like this:

public int Item[int index]
{
   get { ... }
   set { ... }
}

I know I seem dumb, but to me it doesn't seem logical to have the item itself manage it's own collection. To me it seems more logical to have the owning class manage a collection of its subordinates.

 

Here's the code I would like to use:

using System;
using System.Collections.Generic;

public class Collector
{
private List<Item> _collection;

public Item Items[int index]
{
	get { return this._collection[index]; }
	set { this._collection[index] = (Item)value; }
}

// Other members, properties, and methods

public class Item
{
	// Members, properties, and methods
}
}

Do any of you have any advice?

 

 

Regards,

 

 

Brent

Edited by brentnewbury
  • *Experts*
Posted

The first thing that comes to mind is exposing the collection:

using System;
using System.Collections.Generic;

public class Collector
{
private List<Item> _collection;

public List<Item> Items
{
	get { return this._collection; }
}

// Other members, properties, and methods
public class Item
{
	// Members, properties, and methods
}
}

 

-ner

"I want to stand as close to the edge as I can without going over. Out on the edge you see all the kinds of things you can't see from the center." - Kurt Vonnegut
Posted
The first thing that comes to mind is exposing the collection:

using System;
using System.Collections.Generic;

public class Collector
{
private List<Item> _collection;

public List<Item> Items
{
	get { return this._collection; }
}

// Other members, properties, and methods
public class Item
{
	// Members, properties, and methods
}
}

 

-ner

Well bloody hell! I've just been searching for bloody hours trying to find stuff about named indexers (e.g. System.Runtime.CompilerServices.CSharp.IndexName("..."), System.Runtime.CompilerServices.IndexerName("..."), etc).

 

I'm bloody annoyed with myself! But why don't Microsoft just have named indexers, why is this keyword compulsory? Are they trying to enforce a wierred and wonderful object oriented ideal?

  • *Experts*
Posted

It depends on what you want to do, using the indexer on "this" is valid if that's what you wanted - but you described something else.

 

Suppose you had this code to create a Collector:

Collector c = new Collector();

 

Now, if you wanted to index into c itself, then you'd use the "this" indexer:

// Inside the class...
   public int this[int index]
   {
       get { ... }
       set { ... }
   }

// The code using the class:
Collector c = new Collector();
c[0].Field = ""; // The [0] uses the indexer

 

If you wanted to have a property named Items, then you'd use the property:

// Inside the class...
   public List<Item> Items
   {
       get { ... }
   }

// The code using the class:
Collector c = new Collector();
c.Items[0].Field = "";

 

You can, of course, implement both if you wanted to. That would make it look like the "Items" property was a "default" of the class (so that c[0] and c.Items[0] are the same thing). It really depends on what you want.

 

Since your class is called Collector, I'm guessing its purpose is solely to collect things - in that case I'd use the indexer and not expose an Items property. For that matter, if you don't really need your own class, you could just declare instances of List<Item> as needed.

 

-ner

"I want to stand as close to the edge as I can without going over. Out on the edge you see all the kinds of things you can't see from the center." - Kurt Vonnegut
Posted
It depends on what you want to do, using the indexer on "this" is valid if that's what you wanted - but you described something else.

 

Suppose you had this code to create a Collector:

Collector c = new Collector();

 

Now, if you wanted to index into c itself, then you'd use the "this" indexer:

// Inside the class...
   public int this[int index]
   {
       get { ... }
       set { ... }
   }

// The code using the class:
Collector c = new Collector();
c[0].Field = ""; // The [0] uses the indexer

 

If you wanted to have a property named Items, then you'd use the property:

// Inside the class...
   public List<Item> Items
   {
       get { ... }
   }

// The code using the class:
Collector c = new Collector();
c.Items[0].Field = "";

 

You can, of course, implement both if you wanted to. That would make it look like the "Items" property was a "default" of the class (so that c[0] and c.Items[0] are the same thing). It really depends on what you want.

 

Since your class is called Collector, I'm guessing its purpose is solely to collect things - in that case I'd use the indexer and not expose an Items property. For that matter, if you don't really need your own class, you could just declare instances of List<Item> as needed.

 

-ner

The problem with public List<Item> Items {} is that you expose the entire underlying collection. Declaring the collection as readonly seems to have problems of it's own.

 

I don't understand why Microsoft just doesn't expand System.Runtime.CompilerServices.IndexerName("...") for use with all languages under the .NET framwork and not just that ones that don't natively support indexers.

 

There must be some good reason. Atleast I hope there is. Come on Microsoft, I want to know why, give me the details, I want to understand!

Posted

Eureaka!

 

The best solution I've come up with so far is:

public IList<Item> Items
{
get { return this._collection.AsReadOnly(); }
}

I've attached a working solution (see bellow). The idea behind this is to stop any abuse due to exposing the underlying collection, like so:

public List<Item> Items
{
get { return this._collection; }
}

 

Give me a shout if you see any problems :). (Apart from the run-time checking)

 

 

- Brent

NamedIndexer.zip

  • Leaders
Posted

The whole point of indexers is to create classes which behave in the same manner (or very similar) as an array. With this in mind, it doesn't make much sense to create an indexer property for an object that doesn't act like an collection itself, but rather simply contains a collection.

 

I understand that you want to create a property that acts like an array, but using an indexer in that fasion means you lose a whole lot of functionality. When you return an indexed object, you can perform a foreach loop on it (assuming that it is enumerable), you can store a reference to the collection if you need to access it repeatedly, and if the collection programmer so desires, you can modify the collection by adding and removing items and call any other function that the programmer decided to implement. When you use a VB style indexed property, all you get is a property get and a property set.

[sIGPIC]e[/sIGPIC]
Posted
The whole point of indexers is to create classes which behave in the same manner (or very similar) as an array. With this in mind, it doesn't make much sense to create an indexer property for an object that doesn't act like an collection itself, but rather simply contains a collection.

 

I understand that you want to create a property that acts like an array, but using an indexer in that fasion means you lose a whole lot of functionality. When you return an indexed object, you can perform a foreach loop on it (assuming that it is enumerable), you can store a reference to the collection if you need to access it repeatedly, and if the collection programmer so desires, you can modify the collection by adding and removing items and call any other function that the programmer decided to implement. When you use a VB style indexed property, all you get is a property get and a property set.

I have a Article Class that has a list of Pages. To me, myArticle[0]; seems to completed ignore the relationship between an Article and it's Pages. myArtical.Page[0]; seems to model the relationship quite well between an Article and it's Pages..

 

I'm finding it difficult to articulate what I have in my head. Regardless of the programatical limitation of the framework, this seems the better model of the problem space. Personally, I'd say Microsoft was wrong in it's implimentation.

 

Just to reitterate myArticle[0]; seems like an array of Articles, whereas myArticle.Page[0]; is definately a collection of pages that are attached to a particular instance of an Article.

  • Leaders
Posted

First of all, this is not a limitation of the .Net framework, but a limitation of C#. Visual Basic can do parameterized properties, which are like indexers on steroids, but this was left out of C# intentionally. Why? Let's look at the hypothetical Article class.

 

Needless to say, the Article class represents an article. An article is composed of pages, but it also has attributes that apply to an article as a whole. In the world of programming, these would be represented as properties, such as the TypeOfPaper, Title, and TypeOfBinding properties. Pages are represented by the Page class, but how we deal with the Page objects is a matter for debate.

 

Since C# does not support parametized properties, we could use a function to obtain page objects: the hypothetical GetPage(int pageNumber) function. This representation implies that each page is a separate object relationally dependant upon only the Article class that contains it. While there is nothing really wrong with this, there are other ways of looking at it that Microsoft prefers.

 

In the real world, a group of objects has its own inherent properties, which are separate from the properties of whatever may contain the group. Microsoft has designed C# with the intent that programmers will see groups in this manner. When we apply this concept to our program, we should represent the group of pages itself as a class, which would be the hypothetical PageCollection class. The PageCollection class has a Count property, an Add method to add pages, and a Remove method to remove pages, allowing us to treat not only the Article and each Page as an object, but to also treat the group of Pages as an object. Our Article class not only exposes but internally manages its pages through a PageCollection class. This is just a step towards an object oriented point of view.

 

The entire .Net framework follows this design pattern. When you access the items in a ListBox, you don't write ListBox[3] to get the fourth item. You write ListBox.Items[3]. And the items property returns a collection rather than a single item, so that you can also do things like ListBox.Items.Clear(). If you want to access pages by number, but don't want to treat the pages as a group, I would advise that you simply use the GetPage(int pageIndex) method instead of a property.

[sIGPIC]e[/sIGPIC]
Posted
First of all, this is not a limitation of the .Net framework, but a limitation of C#. Visual Basic can do parameterized properties, which are like indexers on steroids, but this was left out of C# intentionally. Why? Let's look at the hypothetical Article class.

 

Needless to say, the Article class represents an article. An article is composed of pages, but it also has attributes that apply to an article as a whole. In the world of programming, these would be represented as properties, such as the TypeOfPaper, Title, and TypeOfBinding properties. Pages are represented by the Page class, but how we deal with the Page objects is a matter for debate.

 

Since C# does not support parametized properties, we could use a function to obtain page objects: the hypothetical GetPage(int pageNumber) function. This representation implies that each page is a separate object relationally dependant upon only the Article class that contains it. While there is nothing really wrong with this, there are other ways of looking at it that Microsoft prefers.

 

In the real world, a group of objects has its own inherent properties, which are separate from the properties of whatever may contain the group. Microsoft has designed C# with the intent that programmers will see groups in this manner. When we apply this concept to our program, we should represent the group of pages itself as a class, which would be the hypothetical PageCollection class. The PageCollection class has a Count property, an Add method to add pages, and a Remove method to remove pages, allowing us to treat not only the Article and each Page as an object, but to also treat the group of Pages as an object. Our Article class not only exposes but internally manages its pages through a PageCollection class. This is just a step towards an object oriented point of view.

 

The entire .Net framework follows this design pattern. When you access the items in a ListBox, you don't write ListBox[3] to get the fourth item. You write ListBox.Items[3]. And the items property returns a collection rather than a single item, so that you can also do things like ListBox.Items.Clear(). If you want to access pages by number, but don't want to treat the pages as a group, I would advise that you simply use the GetPage(int pageIndex) method instead of a property.

Damn! I finally get what you mean. Like a prat I just didn't think about proper collection classes! (I have been awake for 28 hours :yawn: :)

 

Thank you for the advise.

Posted
Damn! I finally get what you mean. Like a prat I just didn't think about proper collection classes! (I have been awake for 28 hours :yawn: :)

 

Thank you for the advise.

Just tried it then realise the reason why I didn't use this method; I don't want anything outside the Article class to be able to add/remove pages. The reason for this is that the Article constructor takes one parameter; the Article ID. From there the constructor initialises the class and sets up pages etc. All I need to do is get pages, so to be honest, I think I may just use a standard public Page GetPage(int pageNumber) {}.

 

Thanks alot for all your help. Like I said, I didn't get any sleep last night. :yawn:

:D

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...