about IndexOf

T

TonyJ

Hello!

Below is simple working program(main is missing) that works but I wonder
about a few things and that has to do with the IndexOf that is used.
IndexOf calls the overrided Equals below.
My first question is: Is it really necessary to have the GetHashCode it
just calls the name.GetHashCode() so what does this do.
My second question is that a type check is done in Equals to check that
"other is Name" is that a good thing to have or can it be removed.
My third question is if it's better to rewrite the overridden Equals in this
way instead or is it just a matter of taste.
public override bool Equals(object other)
{
return (other is Name) && ((Name)other).name == name;
// return (other is Name) && Equals((Name)other);
}


namespace Indexers
{
struct Name
{
private string name;

public Name(string text)
{
this.name = text;
}

public string Text
{
get { return this.name; }
}

public override int GetHashCode()
{
return name.GetHashCode();
}

public override bool Equals(object other)
{
return (other is Name) && Equals((Name)other);
}

public bool Equals(Name other)
{
return this.name == other.name;
}
}
}



namespace Indexers
{
using System;

sealed class PhoneBook
{
private int used;
private Name[] names;
private PhoneNumber[] phoneNumbers;

public PhoneBook()
{
int initialSize = 0;
this.used = 0;
this.names = new Name[initialSize];
this.phoneNumbers = new PhoneNumber[initialSize];
}

public void Add(Name name, PhoneNumber number)
{
enlargeIfFull();
this.names[used] = name;
this.phoneNumbers[used] = number;
this.used++;
}

public Name this[PhoneNumber number]
{
get
{
int i = Array.IndexOf(this.phoneNumbers, number);
if (i != -1)
return this.names;
else
return new Name();
}
}

public PhoneNumber this[Name name]
{
get
{
int i = Array.IndexOf(this.names, name);
if (i != -1)
return this.phoneNumbers;
else
return new PhoneNumber();
}
}

private void enlargeIfFull()
{
if (this.used == this.names.Length)
{
int bigger = used + 16;

Name[] moreNames = new Name[bigger];
this.names.CopyTo(moreNames, 0);

PhoneNumber[] morePhoneNumbers = new PhoneNumber[bigger];
this.phoneNumbers.CopyTo(morePhoneNumbers, 0);

this.names = moreNames;
this.phoneNumbers = morePhoneNumbers;
}
}
}
}


namespace Indexers
{
struct PhoneNumber
{
private string number;

public PhoneNumber(string text)
{
this.number = text;
}

public string Text
{
get { return this.number; }
}

public override int GetHashCode()
{
return this.number.GetHashCode();
}

public override bool Equals(object other)
{
return (other is PhoneNumber) && Equals((PhoneNumber)other);
}

public bool Equals(PhoneNumber other)
{
return this.number == other.number;
}


}
}


namespace Indexers
{
partial class Form1 : Form
{
private PhoneBook phoneBook = new PhoneBook();

public Form1()
{
InitializeComponent();
}

private void findPhone_Click(object sender, System.EventArgs e)
{
string text = name.Text;
if (text != "")
{
phoneNumber.Text = phoneBook[new Name(text)].Text;
}
}

private void findName_Click(object sender, System.EventArgs e)
{
string text = phoneNumber.Text;
if (text != "")
{
name.Text = phoneBook[new PhoneNumber(text)].Text;
}
}

private void add_Click(object sender, System.EventArgs e)
{
if (name.Text != "" && phoneNumber.Text != "")
{
phoneBook.Add(new Name(name.Text),
new PhoneNumber(phoneNumber.Text));
name.Text = "";
phoneNumber.Text = "";
}
}
}
}


//Tony
 
J

Jon Skeet [C# MVP]

Below is simple working program(main is missing) that works but I wonder
about a few things and that has to do with the IndexOf that is used.
IndexOf calls the overrided Equals below.
My first question is: Is it really necessary to have the GetHashCode it
just calls the name.GetHashCode() so what does this do.

It means you can put entries into a hashtable properly. It's possible
that the implementation of ValueType.GetHashCode would do the right
thing, but probably slowly.
My second question is that a type check is done in Equals to check that
"other is Name" is that a good thing to have or can it be removed.

If you take the check out, then you'll get an exception if you try to
compare a Name with something other than a Name. Not good.
My third question is if it's better to rewrite the overridden Equals in this
way instead or is it just a matter of taste.
public override bool Equals(object other)
{
return (other is Name) && ((Name)other).name == name;
// return (other is Name) && Equals((Name)other);

}

It's better for one implementation to call the other - otherwise
you've got duplicate code, and the possibility of the two getting out
of sync.

Jon
 
T

TonyJ

Hello!!

I didn't understand what you meant on my third question.
Was it better to have version A or B for the overridden Equals.

Version A
public override bool Equals(object other)
{
return (other is Name) && ((Name)other).name == name;
}
or Version B
public override bool Equals(object other)
{
return (other is Name) && Equals((Name)other);
}

//Tony
 
J

Jon Skeet [C# MVP]

TonyJ said:
I didn't understand what you meant on my third question.
Was it better to have version A or B for the overridden Equals.

Version A
public override bool Equals(object other)
{
return (other is Name) && ((Name)other).name == name;
}
or Version B
public override bool Equals(object other)
{
return (other is Name) && Equals((Name)other);
}

Version B. That way you've only got the actual equality check (for the
equality of "name" fields) in one place.
 
P

Peter Duniho

Version B. That way you've only got the actual equality check (for the
equality of "name" fields) in one place.

I agree with that generally (not having copied code). But it begs the
question: why the need for the non-virtual Name.Equals(Name) method in the
first place? If you've already got the virtual Name.Equals(Object) and it
does the right thing, why bother with the other method?

I would think that just having a single method is better in this case,
rather than having two, one of which just does the type check before
calling the other. The type check isn't that expensive, if I recall
correctly, nor is the overhead for the call to the virtual method, and
avoiding those performance hits is the only reason I can think of for
splitting the behavior up.

Are there places in .NET or C# where having the non-virtual Equals()
method is particularly useful?

Pete
 
J

Jon Skeet [C# MVP]

Peter Duniho said:
I agree with that generally (not having copied code). But it begs the
question: why the need for the non-virtual Name.Equals(Name) method in the
first place? If you've already got the virtual Name.Equals(Object) and it
does the right thing, why bother with the other method?

Two reasons:

1) It's nice to have a strongly typed API in general
2) In particular, with value types it avoided boxing. If I do:

Name n1 = ...;
Name n2 = ...;

Console.WriteLine (n1.Equals(n2));

that doesn't require any boxing.

It would be nice to make the class implement IEquatable<Name> (given
that it already provides the methods) so that anything wanting to check
for equality in a generic way knows it can do it can use Name
appropriately.
I would think that just having a single method is better in this case,
rather than having two, one of which just does the type check before
calling the other. The type check isn't that expensive, if I recall
correctly, nor is the overhead for the call to the virtual method, and
avoiding those performance hits is the only reason I can think of for
splitting the behavior up.

Are there places in .NET or C# where having the non-virtual Equals()
method is particularly useful?

Well, anything wanting to implement IEquatable<T> needs to implement it
- an alternative is to use a separate IEqualityComparer<T> of course.
 
P

Peter Duniho

[...]
If you've already got the virtual Name.Equals(Object) and it
does the right thing, why bother with the other method?

Two reasons:

1) It's nice to have a strongly typed API in general
2) In particular, with value types it avoided boxing.

Ah. I missed that Name was a struct.

I really am having trouble focusing lately. :)
 
T

TonyJ

Hello!!
I agree with that generally (not having copied code). But it begs the
question: why the need for the non-virtual Name.Equals(Name) method in the
first place? If you've already got the virtual Name.Equals(Object) and it
does the right thing, why bother with the other method?

Two reasons:
1) It's nice to have a strongly typed API in general
What do you mean with a strongly typed API in general?
2) In particular, with value types it avoided boxing. If I do:
Name n1 = ...;
Name n2 = ...;

Console.WriteLine (n1.Equals(n2));

that doesn't require any boxing

I know what boxing is but I can't understand what you mean with the second
statement " In particular, with value types it avoided boxing. If I do"
I can't see what this has to do if I use Version A or Version B for the
overridden Equals.
Can you explain what you mean?

//Tony
 
J

Jon Skeet [C# MVP]

TonyJ said:
What do you mean with a strongly typed API in general?

It's nice to have methods which refer to particular types rather than
just "object" in their parameters, if you actually know the relevant
types beforehand.
I know what boxing is but I can't understand what you mean with the second
statement " In particular, with value types it avoided boxing. If I do"
I can't see what this has to do if I use Version A or Version B for the
overridden Equals.
Can you explain what you mean?

If you have two Equals methods:

Equals(Name name)
and
Equals(object name)

then n1.Equals(n2) will call the first overload, avoiding boxing. If
you only have

Equals(object name)

then n1.Equals(n2) will cause boxing.

Notice the context of my answer - it was when Peter asked why there was
a need for Name.Equals(Name) in the first place, not the implementation
of Name.Equals(Object).
 
T

TonyJ

Thanks!

Good explained!

//Tony

Jon Skeet said:
It's nice to have methods which refer to particular types rather than
just "object" in their parameters, if you actually know the relevant
types beforehand.


If you have two Equals methods:

Equals(Name name)
and
Equals(object name)

then n1.Equals(n2) will call the first overload, avoiding boxing. If
you only have

Equals(object name)

then n1.Equals(n2) will cause boxing.

Notice the context of my answer - it was when Peter asked why there was
a need for Name.Equals(Name) in the first place, not the implementation
of Name.Equals(Object).
 

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