Class Hierarchy design problems...

M

Mark Broadbent

Consider the following statements
//-------
Item i = Basket.Items["Orange"]; //indexer is used to return instance of
class Item

Basket.Items["Apple"].Remove(); //method on class item is fired

item i = new ("Pear");
Basket.Items.Add(item);
//------

Now my class design would be as follows

class Basket{
public Items Items{get;}
}
class Items{
public Add(Item item){...}
public Item this[string item]{get;}
}
class Item{
public void Remove(){...} //problem is here
}

Now my problem is thus. If my Item class was self contained (in the sense
that no other objects contain references to this item), I could implement
the remove method to remove the item. Unfortunately the item must be deleted
from a collection contained within Items. Problem is that since I need the
indexer of Items to return a type of Item, the implementation of Remove must
be put in Item *yet* there is no reference to the Items instance for me to
be able to remove the item from the collection. How can I takle this?

p.s. I should say that I do not want to implement a Remove method within
items because I want to be able to use the indexer[].Remove design as above.

Any ideas?

Best Regards,

Mark.
 
D

David Browne

Mark Broadbent said:
Consider the following statements
//-------
Item i = Basket.Items["Orange"]; //indexer is used to return instance of
class Item

Basket.Items["Apple"].Remove(); //method on class item is fired

item i = new ("Pear");
Basket.Items.Add(item);
//------

Now my class design would be as follows

class Basket{
public Items Items{get;}
}
class Items{
public Add(Item item){...}
public Item this[string item]{get;}
}
class Item{
public void Remove(){...} //problem is here
}

Now my problem is thus. If my Item class was self contained (in the sense
that no other objects contain references to this item), I could implement
the remove method to remove the item. Unfortunately the item must be
deleted from a collection contained within Items. Problem is that since I
need the indexer of Items to return a type of Item, the implementation of
Remove must be put in Item *yet* there is no reference to the Items
instance for me to be able to remove the item from the collection. How can
I takle this?

p.s. I should say that I do not want to implement a Remove method within
items because I want to be able to use the indexer[].Remove design as
above.

Any ideas?

Typically you would add a reference to the Items container in each item.
Then Item.Remove can invoke Items.Remove(Item).

David
 
N

Nick Malik [Microsoft]

Consider the following statements
//-------
Item i = Basket.Items["Orange"]; //indexer is used to return instance of
class Item

Basket.Items["Apple"].Remove(); //method on class item is fired

item i = new ("Pear");
Basket.Items.Add(item);
//------

Interesting.

My opinion is that your intent doesn't make sense. Doesn't an apple exist
outside of the basket?
How does the fact that the apple is in a basket influence the nature of the
apple itself? Should it's properties change? Does it become more tasty, or
does it decay more slowly? In fact, if you were a microbe just under the
skin of the apple, would you be able to detect any change at all in the
apple at the point when it is placed into, or removed from, the basket?

I suspect that, if you consider these ideas more carefully, you will see
that it is not a correct assumption that "Add" or "Remove" should be methods
of the item. They are methods of the basket's list of items.

In fact, you have created an unequal heirarchy of operations above, by
placing the Add method on "Items" but the Remove method on the individual
item. Is there no Remove method on the items list itself? If so, how do
you pick "any" item from the basket?

You feel that your code is more elegant by writing:
Basket.Items["Apple"].Remove(); //method on class item is fired

but is it more understandable to the developer who follows you, and who
looks at the code for the Items list and sees no Remove method?

Is it THAT MUCH more elegant than writing the following?
Basket.Items.Remove(Items["Apple"]);

(sorry for writing most of my response in questions. It is late at night...
I'm in a questioning mood. :)
--
--- Nick Malik [Microsoft]
MCSD, CFPS, Certified Scrummaster
http://blogs.msdn.com/nickmalik

Disclaimer: Opinions expressed in this forum are my own, and not
representative of my employer.
I do not answer questions on behalf of my employer. I'm just a
programmer helping programmers.
--
Mark Broadbent said:
Consider the following statements
//-------
Item i = Basket.Items["Orange"]; //indexer is used to return instance of
class Item

Basket.Items["Apple"].Remove(); //method on class item is fired

item i = new ("Pear");
Basket.Items.Add(item);
//------

Now my class design would be as follows

class Basket{
public Items Items{get;}
}
class Items{
public Add(Item item){...}
public Item this[string item]{get;}
}
class Item{
public void Remove(){...} //problem is here
}

Now my problem is thus. If my Item class was self contained (in the sense
that no other objects contain references to this item), I could implement
the remove method to remove the item. Unfortunately the item must be
deleted from a collection contained within Items. Problem is that since I
need the indexer of Items to return a type of Item, the implementation of
Remove must be put in Item *yet* there is no reference to the Items
instance for me to be able to remove the item from the collection. How can
I takle this?

p.s. I should say that I do not want to implement a Remove method within
items because I want to be able to use the indexer[].Remove design as
above.

Any ideas?

Best Regards,

Mark.
 
M

Mark Broadbent

David, Nick thanks for the input.
I agree (as you are both suggesting) that perhaps my problem is down to an
incorrect design, although I am not entirely sure it is as clean cut as
that.
The addition of a Remove method on the Items class would probably be a good
addition too i.e. Remove(Item item), however I do still think it makes
'sense' to have methods that fire on an object (in this scenario) which
operate in the context of the hierarchy.
For instance I would be saying that ... In that basket, choose an item (the
apple), and remove it.
You may both disagree entirely?

The question I would like to have answered though is this situation
possible?
The biggest problem is that the indexer is determining the subsequent
hierarchy due to its returned type (and therefore the methods would need to
exist on that type (which causes the problem of being able to reference the
items class).
The only work around I can think off is to create a new type called for
instance ItemEditor which I can instanciate and pass a reference to the
collection stored in the Items class . I could therefore return the
ItemEditor via the Items indexer (providing these methods). The problem
arises then that I would need to create an Item property in the Items editor
to return an object of type Item.
e.g.
class ItemEditor{
Item item;
Hashtable itemlist;
public ItemEditor(Hashtable itemcollection, Item item){...} //note
the constructor sets its localvars to these references so I can operate on
them
public Item
Item{ //item
property to return type Item
return item;
}
public void
Remove(){ //remove
method that can operate on collection within Items class
itemlist.Remove(item.Name);
}
}

//therefore class Items would need to be...
class Items{
Hashtable itemlist;
public Add(Item item){...}
public Item this[string item]{
get { Item i = itemlist[item];
return new ItemEditor(itemlist, item);
}
}
}

//Therefore the following would work as thus :-
//-------
Item i = Basket.Items["Orange"].Item;
Basket.Items["Apple"].Remove(); //method on class ItemEditor is fired

item i = new ("Pear");
Basket.Items.Add(item);
//------

Is there a way to do this any differently (i.e. to have the indexer
returning a certain type such as Items) but have addtional methods available
which are not present in Items? e.g.
Basket.Items["Apple"]; //returns type Item through indexer
Basket.Items["Apple"].Remove(); //item does not have remove
implemented (it is implemented elsewhere?)

It would be just good to know what is available to me.

Thanks guys,

Br,

Mark.
===================================





Mark Broadbent said:
Consider the following statements
//-------
Item i = Basket.Items["Orange"]; //indexer is used to return instance of
class Item

Basket.Items["Apple"].Remove(); //method on class item is fired

item i = new ("Pear");
Basket.Items.Add(item);
//------

Now my class design would be as follows

class Basket{
public Items Items{get;}
}
class Items{
public Add(Item item){...}
public Item this[string item]{get;}
}
class Item{
public void Remove(){...} //problem is here
}

Now my problem is thus. If my Item class was self contained (in the sense
that no other objects contain references to this item), I could implement
the remove method to remove the item. Unfortunately the item must be
deleted from a collection contained within Items. Problem is that since I
need the indexer of Items to return a type of Item, the implementation of
Remove must be put in Item *yet* there is no reference to the Items
instance for me to be able to remove the item from the collection. How can
I takle this?

p.s. I should say that I do not want to implement a Remove method within
items because I want to be able to use the indexer[].Remove design as
above.

Any ideas?

Best Regards,

Mark.
 
D

David Browne

Mark Broadbent said:
David, Nick thanks for the input.
I agree (as you are both suggesting) that perhaps my problem is down to an
incorrect design, although I am not entirely sure it is as clean cut as
that.
The addition of a Remove method on the Items class would probably be a
good addition too i.e. Remove(Item item), however I do still think it
makes 'sense' to have methods that fire on an object (in this scenario)
which operate in the context of the hierarchy.
For instance I would be saying that ... In that basket, choose an item
(the apple), and remove it.
You may both disagree entirely?

It's a question of modeling. If your container is a Basket and your item is
an Apple, then the item should not have a Remove() method. As Nick said, an
Apple's state does not change when you put it in a basket. You can put
non-Apples in a Basket, and you can put Apples in a non-Basket. It makes no
sense for the Apple to hold a reference to the Basket. And so
Basket.Remove(Apple)
is a better model than
Apple.RemoveFromBaket()

However if your container is, say, an Organization and your item is an
Employee, then the situation is different. An Employee would naturally have
a reference to its Organization, and can use that reference to invoke
methods on the Organization.
The question I would like to have answered though is this situation
possible?
The biggest problem is that the indexer is determining the subsequent
hierarchy due to its returned type (and therefore the methods would need
to exist on that type (which causes the problem of being able to reference
the items class).


There is no problem. The Item should have a reference to the container in
all those situations where it is appropriate for the item to remove itself.

David
 
M

Mark Broadbent

Sorry I'm not explaining myself very well. Thanks for all your input so far.
Of course, my object hierarchy is slightly more complex than my Basket/item
model (and probably more suited for what I am trying to do).
I guess what I am trying to say is....

1. is there a way for me a access a "parent" container's variables from a
child object in the hierarchy, without having to explicitly pass references
down e.g. through a constructor. I'm thinking there might be something like
a parent or caller property? Subtypes/ subclasses for instance have the base
accessor (I know that is completely different scenario though).
2.is there a way to return a type from an indexer *but* provide additional
methods to a caller if the type is being accessed from a hierarchy using
said indexer?
e.g.
Item item = new Item("Apple");
Item item2;
Basket.Items.Add(item);
Basket.Items[item].Remove(); //would be available in this scenario,
whereas...
item2 = Basket.Items[item]; //item2 now points to the item in the
collection
item2.Remove() //NEEDS TO BE INVALID BECAUSE item is not accessed via
Basket

I'm not too bothered about the Basket example, just eager to know if points
1 and 2 are possible (and how to implement them).

Best Regards,

Mark.
 
J

Joanna Carter \(TeamB\)

1. is there a way for me a access a "parent" container's variables from a
child object in the hierarchy, without having to explicitly pass references
down e.g. through a constructor. I'm thinking there might be something like
a parent or caller property? Subtypes/ subclasses for instance have the base
accessor (I know that is completely different scenario though).

If you have to access containers from items in the container, then I would
seriously question your design. This kind of requirement is usually only
valid in things like tree structures. As others have said, Item classes
should not possess any knowledge of container methods like Add or Remove;
these are methods that are the sole responsibility of the container.
2.is there a way to return a type from an indexer *but* provide additional
methods to a caller if the type is being accessed from a hierarchy using
said indexer?
e.g.
Item item = new Item("Apple");
Item item2;
Basket.Items.Add(item);
Basket.Items[item].Remove(); //would be available in this scenario,
whereas...
item2 = Basket.Items[item]; //item2 now points to the item in the
collection
item2.Remove() //NEEDS TO BE INVALID BECAUSE item is not accessed via
Basket

I'm not too bothered about the Basket example, just eager to know if points
1 and 2 are possible (and how to implement them).

Simply this: do not do what you are attempting it is the entrance to a world
of problems.

Joanna
 
D

David Browne

Mark Broadbent said:
Sorry I'm not explaining myself very well. Thanks for all your input so
far.
Of course, my object hierarchy is slightly more complex than my
Basket/item model (and probably more suited for what I am trying to do).
I guess what I am trying to say is....

1. is there a way for me a access a "parent" container's variables from a
child object in the hierarchy, without having to explicitly pass
references down e.g. through a constructor. I'm thinking there might be
something like a parent or caller property? Subtypes/ subclasses for
instance have the base accessor (I know that is completely different
scenario though).

No. This is not possible. You would have to pass a reference.
2.is there a way to return a type from an indexer *but* provide additional
methods to a caller if the type is being accessed from a hierarchy using
said indexer?

Not really no. If Basket.Items[item] is an expression of some type. If it
has a Remove() method then that method will be accessable from a variable of
that type the same as from an expression.

Do you really feel that the syntax
Basket.Items[item].Remove();
is somehow better than
Basket.Remove(item);
?

David
 
A

Alan Pretre

Joanna Carter (TeamB) said:
If you have to access containers from items in the container, then I would
seriously question your design. This kind of requirement is usually only
valid in things like tree structures.

Don't think I completely agree with what you say here.

Let's imagine a person in a room full of buddies. He jokes around and is
free in his conversation. Now, put this same person in a room with
strangers. He of course is the same person, but his behavior will most
likely be different, because he has been placed into a different context.

So we see that the environment in which an object finds itself may affect
its behavior, and in some cases, its state.
As others have said, Item classes
should not possess any knowledge of container methods like Add or Remove;
these are methods that are the sole responsibility of the container.

Agreed (the encapsulation principle). But it is not unreasonable to notify
the contained object that his environment has changed.

-- Alan
 
M

Mark Broadbent

Replies....

David Browne said:
No. This is not possible. You would have to pass a reference.

Thanks, I guessed that might be the response
2.is there a way to return a type from an indexer *but* provide
additional methods to a caller if the type is being accessed from a
hierarchy using said indexer?

Not really no. If Basket.Items[item] is an expression of some type. If
it has a Remove() method then that method will be accessable from a
variable of that type the same as from an expression.
Understood.


Do you really feel that the syntax
Basket.Items[item].Remove();
is somehow better than
Basket.Remove(item);

No, not really, but was thinking of providing both. Was more interested in
looking at all possibilities.
If I really wanted to do this I've thought of another way which is probably
be best way of implementing this kind of functionality. I guess the real
(programmatic) problem with the Item type having a remove method is the fact
that Item can be instanciated apart from Items (and therefore would not at
that stage not exist in the Items collection and therefore could not have a
reference to it to remove itself). However in my scenario the item probably
wouldnt exist without being part of the items collection anyhow. I could
therefore use the Items class as an object builder for Item, (and therefore
maintain a private reference to the collection within Item itself at
construction).


Thx for your contributions,

Best Regards,

Mark.
 
M

Mark Broadbent

Thanks to David, Nick, Joanna and Alan.
I guess this thread has come to it's conclusion. I tend to agree with you
all that it is probably best to steer clear of this kind of design if
possible. I mainly like to investigate the possibilities of doing some as
well as whether it is a good design and these questions have been answered.

I do think that in some cases that my scenario may sometimes be acceptable.
I like Alan's example.

Thanks again all.

Best Regards,

Mark.
 
M

Mark Broadbent

Just an update to this, by implementing a self deleting child, it seems
there is a call stack problem when it removes itself when it is the last
item in the collection (probably garbage collection is kicking in). This
would obviously prove to some degree to keep it simple.
Will have a look in the morning though.

Mark Broadbent said:
Thanks to David, Nick, Joanna and Alan.
I guess this thread has come to it's conclusion. I tend to agree with you
all that it is probably best to steer clear of this kind of design if
possible. I mainly like to investigate the possibilities of doing some as
well as whether it is a good design and these questions have been
answered.

I do think that in some cases that my scenario may sometimes be
acceptable. I like Alan's example.

Thanks again all.

Best Regards,

Mark.


Mark Broadbent said:
Consider the following statements
//-------
Item i = Basket.Items["Orange"]; //indexer is used to return instance of
class Item

Basket.Items["Apple"].Remove(); //method on class item is fired

item i = new ("Pear");
Basket.Items.Add(item);
//------

Now my class design would be as follows

class Basket{
public Items Items{get;}
}
class Items{
public Add(Item item){...}
public Item this[string item]{get;}
}
class Item{
public void Remove(){...} //problem is here
}

Now my problem is thus. If my Item class was self contained (in the sense
that no other objects contain references to this item), I could implement
the remove method to remove the item. Unfortunately the item must be
deleted from a collection contained within Items. Problem is that since I
need the indexer of Items to return a type of Item, the implementation of
Remove must be put in Item *yet* there is no reference to the Items
instance for me to be able to remove the item from the collection. How
can I takle this?

p.s. I should say that I do not want to implement a Remove method within
items because I want to be able to use the indexer[].Remove design as
above.

Any ideas?

Best Regards,

Mark.
 
M

Mark Broadbent

Problem is occurring in my unit test RemoveItemOnEmptyItems.
The problem caused by this design is that the desired behaviour by the Items
indexer is to return a null object it Items hasn't at least one item (or the
parameter passed to the indexer does not exist within the Tags collection)
otherwise it will return the item selected.
Therefore
=====================
Items.Add(item);
Assert.IsTrue(Items[item],item); //will succeed
Assert.IsFalse(Items["noitem"],item); //will succeed
//and finally...
Items["noitem"].Remove(); //will therefore cause an exception since we are
trying to fire a Remove method on a non existant instance
=====================
There are ways to rectify this problem, but I think I have convinced myself
too that this design is flawed, and shall revert it to a more simplistic oo
design.

Thanks again,

Br,

Mark.

Mark Broadbent said:
Just an update to this, by implementing a self deleting child, it seems
there is a call stack problem when it removes itself when it is the last
item in the collection (probably garbage collection is kicking in). This
would obviously prove to some degree to keep it simple.
Will have a look in the morning though.

Mark Broadbent said:
Thanks to David, Nick, Joanna and Alan.
I guess this thread has come to it's conclusion. I tend to agree with you
all that it is probably best to steer clear of this kind of design if
possible. I mainly like to investigate the possibilities of doing some as
well as whether it is a good design and these questions have been
answered.

I do think that in some cases that my scenario may sometimes be
acceptable. I like Alan's example.

Thanks again all.

Best Regards,

Mark.


Mark Broadbent said:
Consider the following statements
//-------
Item i = Basket.Items["Orange"]; //indexer is used to return instance of
class Item

Basket.Items["Apple"].Remove(); //method on class item is fired

item i = new ("Pear");
Basket.Items.Add(item);
//------

Now my class design would be as follows

class Basket{
public Items Items{get;}
}
class Items{
public Add(Item item){...}
public Item this[string item]{get;}
}
class Item{
public void Remove(){...} //problem is here
}

Now my problem is thus. If my Item class was self contained (in the
sense that no other objects contain references to this item), I could
implement the remove method to remove the item. Unfortunately the item
must be deleted from a collection contained within Items. Problem is
that since I need the indexer of Items to return a type of Item, the
implementation of Remove must be put in Item *yet* there is no reference
to the Items instance for me to be able to remove the item from the
collection. How can I takle this?

p.s. I should say that I do not want to implement a Remove method within
items because I want to be able to use the indexer[].Remove design as
above.

Any ideas?

Best Regards,

Mark.
 
J

Joanna Carter \(TeamB\)

//and finally...
Items["noitem"].Remove(); //will therefore cause an exception since we are
trying to fire a Remove method on a non existant instance
=====================
There are ways to rectify this problem, but I think I have convinced myself
too that this design is flawed, and shall revert it to a more simplistic oo
design.

So now you know why we suggested changing your design :))

Take a look at books like Design Patterns; they are there because folks have
made most of the mistakes and have now documented the best ways to do
things.

I know it's always fun to try and re-invent the wheel, but it has been
proven that the best shape is usually round :))

Joanna
 
B

Bill Butler

Hi Mark,

I threw together a quick implementation of your design concept.
It works fine (it needs a parent reference).
It is really not very different from a C style tree structure where you ask
a Node to remove itself from a tree
-------------------- Quick and dirty code alert ------------------------

using System;
using System.Collections;

namespace Stupidhead
{

public class TEST
{
private Basket basket= new Basket();

public void Run()
{
for(int i = 0; i < 10 ; i++)
loop();
}

private void loop()
{
Item a = new Item("AAAAAA");
Item b = new Item("BBBBBB");
Item c = new Item("CCCCCC");
Item d = new Item("DDDDDD");
basket.Items.Add(a);
basket.Items.Add(b);
basket.Items.Add(c);
basket.Items.Add(d);
a.Remove();
c.Remove();
d.Remove();
b.Remove();
}

static void Main()
{
TEST test = new TEST();
test.Run();
}
}
}

public class Item
{
private string key;
private Items parent;
public Item(string key)
{
Console.WriteLine("new Item({0})",key);
this.parent = null;
this.key = key;
}

public Items Parent {get {return (parent);} set{parent = value;}}
public string Key {get {return (key );}}

public void Remove()
{
Console.WriteLine("Item.Remove()");
if(parent == null)
{
Console.WriteLine("(parent = null)");
return;
}
parent.Remove(key);
}
}

public class Items
{
Hashtable data;
public Items()
{
Console.WriteLine("new Items()");
data = new Hashtable();
}

public void Add(Item item)
{
Console.WriteLine("Items.Add({0})",item.Key);
if (!data.ContainsKey(item.Key))
{
data[item.Key] = item;
item.Parent = this;
}
else Console.WriteLine(" aleady there");
}

public Item this[string key]
{
get
{
if (data.ContainsKey(key))
return (Item)data[key];
else
return (Item) null;
}
}

public void Remove(string key)
{
Console.Write("Items.Remove({0})",key);
if (data.ContainsKey(key))
data.Remove(key);
else
Console.Write(":fail");
Console.WriteLine();
}
}

public class Basket
{
private Items items;

public Basket()
{
Console.WriteLine("new Basket()");
items = new Items();
}
public Items Items{get{return (items);}
}
}
 
B

Bill Butler

Joanna Carter (TeamB) said:
I know it's always fun to try and re-invent the wheel, but it has been
proven that the best shape is usually round :))

Joanna

That may be so....
But lets compare modern wheels with ancient wheels
Spokes/hubs vs a solid one piece design ... nice improvement
Rubber tires vs rock/wood
Treads
Air filled
run while flat

All in all, I would say that the wheel has improved quite a bit.
So reinventing the wheel has proven to be a good idea.

Always starting from scratch is a bad idea.
Attempting to improve on existing designs can be quite a good idea

Bill
 
M

Mark Broadbent

LOL.
Thanks Joanna. I've been using/ studying different areas topics of c# and
oop for a few years now and it's ironic that you mention design pattens. Of
course its just one more thing on my list that I will soon come to (I've had
a Data Structures and Algorithms in Java book on my shelf for a while now. I
know it needs to be read :)

I do definately think there is milage in attempting to bend the wheel from
time to time though even if it is just to know that it is possible and/ or
is not a good idea. I must admit though, I do still like the idea of an
object that can remove itself from its container because it gives more
flexibility and power to that object.
For instance why, if we have a reference to an item should we then be forced
to use it's container to do the lookup and remove for said item? Surely we
should just be able to tell our item to "go home" to the great garbage
collector in the sky directly to it's face (it then notifies it's container
that it wishes to leave)?

Taking the analogy of people in a room. I have to ask myself why couldn't a
person initiate leaving a room themselves rather than a person being told to
leave a room from the loudspeaker on the wall of the room? In this instance
I see scope to having both objects being able to initiate the action (even
though Room is a container of people).

The problem (regardless of improper design or not) being experienced here is
simply and purely being caused by the use of the indexer and it's return of
null (as desired) if the item specified in the indexer has not been found
(as I shall demonstate in my reply to Bill's example).

Thanks again Joanna, I have definately taken on board your comments.

Best Regards,

Mark.

Joanna Carter (TeamB) said:
//and finally...
Items["noitem"].Remove(); //will therefore cause an exception since we
are
trying to fire a Remove method on a non existant instance
=====================
There are ways to rectify this problem, but I think I have convinced myself
too that this design is flawed, and shall revert it to a more simplistic oo
design.

So now you know why we suggested changing your design :))

Take a look at books like Design Patterns; they are there because folks
have
made most of the mistakes and have now documented the best ways to do
things.

I know it's always fun to try and re-invent the wheel, but it has been
proven that the best shape is usually round :))

Joanna
 
M

Mark Broadbent

Hi Bill, great example, nice 1.
It's behaviour is good and esssentially the design and implementation is
very similar to mine. It does unfortunately suffer from the same problem
though.
"An unhandled exception of type 'System.NullReferenceException' occurred in
containerdemo.exe
Additional information: Object reference not set to an instance of an
object."

As you have shown and proved in your example the items successfully remove
themselves from the container. But you'll remember we have said that the
desired behaviour of the indexer is to return null if the requested item is
not found in the container - as you have correctly implemented. It is this
single requirement that is causing the problems.
Now, from your code:-

//following compiles and runs
Item a = new Item("AAAAAA");
basket.Items.Add(a);
a.Remove();

//following does not compile because compiler is detecting use of unassigned
variable
//which is good since we don't want this to compile cos e does not point to
an instance
Item e;
basket.Items.Add(e);
e.Remove();

//following compiles fine, but will throw an exception.
//Whether selected item via indexer exists or not can only be known as
runtime
basket.Items["spannerintheworks"].Remove();

If however your indexer is changed to the following then even the above will
not cause an exception, it does however mean that we have broken the null
desired behaviour rule.
public Item this[string key]
{
get
{
if (data.ContainsKey(key))
return (Item)data[key];
else
return new Item("");
}
}

So really it makes sense what is happening because obviously a null
reference cannot have a Remove method, but the compiler does not know a
Remove method is being called on a null reference till runtime.

Thanks for the time you took on this code and your comments.

Best Regards,
Mark.

Bill Butler said:
Hi Mark,

I threw together a quick implementation of your design concept.
It works fine (it needs a parent reference).
It is really not very different from a C style tree structure where you
ask a Node to remove itself from a tree
-------------------- Quick and dirty code alert ------------------------

using System;
using System.Collections;

namespace Stupidhead
{

public class TEST
{
private Basket basket= new Basket();

public void Run()
{
for(int i = 0; i < 10 ; i++)
loop();
}

private void loop()
{
Item a = new Item("AAAAAA");
Item b = new Item("BBBBBB");
Item c = new Item("CCCCCC");
Item d = new Item("DDDDDD");
basket.Items.Add(a);
basket.Items.Add(b);
basket.Items.Add(c);
basket.Items.Add(d);
a.Remove();
c.Remove();
d.Remove();
b.Remove();
}

static void Main()
{
TEST test = new TEST();
test.Run();
}
}
}

public class Item
{
private string key;
private Items parent;
public Item(string key)
{
Console.WriteLine("new Item({0})",key);
this.parent = null;
this.key = key;
}

public Items Parent {get {return (parent);} set{parent = value;}}
public string Key {get {return (key );}}

public void Remove()
{
Console.WriteLine("Item.Remove()");
if(parent == null)
{
Console.WriteLine("(parent = null)");
return;
}
parent.Remove(key);
}
}

public class Items
{
Hashtable data;
public Items()
{
Console.WriteLine("new Items()");
data = new Hashtable();
}

public void Add(Item item)
{
Console.WriteLine("Items.Add({0})",item.Key);
if (!data.ContainsKey(item.Key))
{
data[item.Key] = item;
item.Parent = this;
}
else Console.WriteLine(" aleady there");
}

public Item this[string key]
{
get
{
if (data.ContainsKey(key))
return (Item)data[key];
else
return (Item) null;
}
}

public void Remove(string key)
{
Console.Write("Items.Remove({0})",key);
if (data.ContainsKey(key))
data.Remove(key);
else
Console.Write(":fail");
Console.WriteLine();
}
}

public class Basket
{
private Items items;

public Basket()
{
Console.WriteLine("new Basket()");
items = new Items();
}
public Items Items{get{return (items);}
}
}
 
B

Bill Butler

Mark Broadbent said:
So really it makes sense what is happening because obviously a null reference cannot have a Remove
method, but the compiler does not know a Remove method is being called on a null reference till
runtime.

You really need to decide how to handle this in a way that makes the most sense for your
application.
You could check for a null Item before calling Remove()
You could add a try block

Returning an empty Item is probably not a good idea unless you check for it.
Other wise you can have this:
Item item = basket.Items["spannerintheworks"]; //returns the empty item;
Func(item); //use it like it was perfectly good
This is probably a bad idea.

perhaps an exception in the case of
basket.Items["spannerintheworks"].Remove();
is a good idea, since you don't actually have the item object.

If you don't actually have the object
basket.Items.Remove(["spannerintheworks");
makes more sense AND it won't throw an exception.
Thanks for the time you took on this code and your comments.

No problem

Bill
 

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