why does IndexOf still return -1

  • Thread starter Thread starter Gordon Cowie
  • Start date Start date
G

Gordon Cowie

Wouldn't it make sense if IndexOf just threw a character not found
exception instead of -1?

If -1 is what you were expecting there should be a function ContainsChar
that returns bool.

Thoughts?
 
what's the difference between a known exception and a known return based
on the same input data? your logic to handle the error would very
likely be the same...

personally, as long as i know what happens when i give a particular
function some data, i don't care how it handles something like that. it
could return -3.14159 or throw a YoureAFoolForTryingThatException, as
long as its in the documentation.
 
jeremiah said:
what's the difference between a known exception and a known return based
on the same input data? your logic to handle the error would very
likely be the same...

personally, as long as i know what happens when i give a particular
function some data, i don't care how it handles something like that. it
could return -3.14159 or throw a YoureAFoolForTryingThatException, as
long as its in the documentation.

It would be "cleaner" as far as separation of the error-handling type of
code goes. Take, for example this function.

int a = SomeString.IndexOf("f");
if (a != -1)
{
int b = SomeString.IndexOf("g");
if (b != -1)
{
int c = SomeString.Substring(a,b).IndexOf("i");
if (c != -1)
{
return(SomeString.Substring(0,c);
}
else
throw InvalidStuff;
}
else
throw InvalidStuff;
}
else
throw InvalidStuff;

phew. now, with exceptions..

try
{
return Somestring.Substring(0,
SomeString.Substring(
SomeString.IndexOf("f"),
SomeString.IndexOf("g")).IndexOf("i"));
}
catch(CharNotFoundException e)
{
throw InvalidStuff;
}

cleaner no? no pesky a,b,c variables either.
 
It would be "cleaner" as far as separation of the error-handling type of
code goes.

But it's not necessarily an error if the string doesn't contain the
specified character.

try
{
return Somestring.Substring(0,
SomeString.Substring(
SomeString.IndexOf("f"),
SomeString.IndexOf("g")).IndexOf("i"));
}
catch(CharNotFoundException e)
{
throw InvalidStuff;
}

You can still do it this way, Substring shouold throw if you end up
passing -1 to it.


Mattias
 
Gordon Cowie said:
jeremiah johnson wrote:
try
{
return Somestring.Substring(0,
SomeString.Substring(
SomeString.IndexOf("f"),
SomeString.IndexOf("g")).IndexOf("i"));
}
catch(CharNotFoundException e)
{
throw InvalidStuff;
}

cleaner no? no pesky a,b,c variables either.

Sure, as long as you want to call SubString a bunch if times and throw away
the values :)

I prefer the -1 in this case much more. An exception should be used (IMHO)
for "exceptional" circumstances - IndexOf not finding a character is a
"normal" outcome for IndexOf. An execption would be useful if the target
string was null (as it does throw), or if c# strings were kept in native
encodings and they didn't match, etc. Another case is when no "out of band"
sentinal value is possible -- like "int GetNextInt()". Although it's a
matter of taste in most cases, I reckon...
 
It would also be far more expensive. Exceptions are very expensive and
really, exceptions are for "exceptional" events, things that you normally
would not have control over.

For instance, if you open a socket and the network suddenly shutdown, well,
that is not something your app could have predicted.
 
Gordon Cowie said:
Wouldn't it make sense if IndexOf just threw a character not found
exception instead of -1?

Not necessarily. In many cases it isn't exceptional.
The real answer is an overload with an extra boolean parameter to indicate
whether you want an exception (You could use a different named method
instead but that would clutter the docs and intellisense more)
If -1 is what you were expecting there should be a function ContainsChar
that returns bool.

No because then if you need the index if it is there but it is optional (a
common case) then you must either incur the exception cost or incur the
search cost twice.

Another reason not to throw exception unless something bad has happened is
that it makes debugging harder - typically when I have a problem I would
tell the debugger to stop on exceptions - If there is one for an
unexceptional case then you need separate exception types and you need to
mess around with telling it which ones to stop for and which not and then
just maybe it really is the source ofthe problem one time. If you use the -1
incorrectly it will usually turn up as an array bounds exception.
 
Peter Rilling said:
It would also be far more expensive. Exceptions are very expensive and
really, exceptions are for "exceptional" events, things that you normally
would not have control over.

Exceptions aren't nearly as expensive as most people think. See
http://www.pobox.com/~skeet/csharp/exceptions.html
(In this particular case the performance penalty could be significant
in many programs, but I don't want you follow the myth of exception
performance :)

However, they're still wrong in this situation, because it really isn't
exceptional for a character not to be present in a string - it's
something which is almost always a possibility when you use IndexOf, in
my experience. It's rarely an error condition which would make you want
to unwind the stack a significant distance.

Of course, if Gordon wants to write his own method which throws an
exception instead of returning -1, there's nothing to stop him -
whereas if the method already threw an exception, wrapping it in a
method which returned -1 would incur the performance penalty of the
exception.
 
Jon,

Note that Exceptions are significantly more expensive in V2, as shown here:

V1.1
Total time taken: 00:00:32.2768033
Exceptions per millisecond: 154

V2
Total time taken: 00:02:59.3157696
Exceptions per millisecond: 27

that means 37 µsecs. per exception, I would say this is more expensive as
some people think ;-)

Willy.


| > It would also be far more expensive. Exceptions are very expensive and
| > really, exceptions are for "exceptional" events, things that you
normally
| > would not have control over.
|
| Exceptions aren't nearly as expensive as most people think. See
| http://www.pobox.com/~skeet/csharp/exceptions.html
| (In this particular case the performance penalty could be significant
| in many programs, but I don't want you follow the myth of exception
| performance :)
|
| However, they're still wrong in this situation, because it really isn't
| exceptional for a character not to be present in a string - it's
| something which is almost always a possibility when you use IndexOf, in
| my experience. It's rarely an error condition which would make you want
| to unwind the stack a significant distance.
|
| Of course, if Gordon wants to write his own method which throws an
| exception instead of returning -1, there's nothing to stop him -
| whereas if the method already threw an exception, wrapping it in a
| method which returned -1 would incur the performance penalty of the
| exception.
|
| --
| Jon Skeet - <[email protected]>
| http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
| If replying to the group, please do not mail me too
 
Willy Denoyette said:
Note that Exceptions are significantly more expensive in V2, as shown here:

V1.1
Total time taken: 00:00:32.2768033
Exceptions per millisecond: 154

V2
Total time taken: 00:02:59.3157696
Exceptions per millisecond: 27

that means 37 µsecs. per exception, I would say this is more expensive as
some people think ;-)

It's much less expensive than some people think though. There was a
thread on one of the groups a while ago where people were blaming poor
web app performance on the fact that it threw 200 exceptions *per
hour*.

In situations where exceptions are logical to use in the first place
(where usually several levels of stack will be unwound) 37 microseconds
is completely insignificant.

Having said all that, it's surprising just how much slower they are in
2.0...
 
Inline

****

Willy.

Willy Denoyette said:
Note that Exceptions are significantly more expensive in V2, as shown
here:

V1.1
Total time taken: 00:00:32.2768033
Exceptions per millisecond: 154

V2
Total time taken: 00:02:59.3157696
Exceptions per millisecond: 27

that means 37 µsecs. per exception, I would say this is more expensive as
some people think ;-)

It's much less expensive than some people think though. There was a
thread on one of the groups a while ago where people were blaming poor
web app performance on the fact that it threw 200 exceptions *per
hour*.

***
I do remember this thread.

In situations where exceptions are logical to use in the first place
(where usually several levels of stack will be unwound) 37 microseconds
is completely insignificant.

***
Agreed, my point was to illustrate that expensive/inexpensive are relative
terms, and that it's good to know how expensive they are in V2 compared to
V1.

Having said all that, it's surprising just how much slower they are in
2.0...

***
There are a number of CLR services which have become more expensive in V2
(due to security hardening), but this is largely compensated by the
performance gains in other domains.
 
Willy Denoyette said:
Agreed, my point was to illustrate that expensive/inexpensive are relative
terms, and that it's good to know how expensive they are in V2 compared to
V1.

That's certainly true - I'll update the page to indicate that when I
get a chance.
 
No because then if you need the index if it is there but it is optional (a
common case) then you must either incur the exception cost or incur the
search cost twice.

I see. Also, from reading other's replies. The real issue is the "cost"
related to exceptions. If they were free I think it would make for a
much cleaner coding style. Probably even anywhere a special return code
is returned.

Another negative about -1. It forces IndexOf to return an int, when a
uint would be more appropriate. The max length of the string is
effectively cut in half in order to provide space for the possible -1
result.
 
One of the bugbears of error handling in any application and any
language is that as an author of a utility function such as IndexOf,
you really have no idea if some situations constitute "exceptions."

Several other posters here have rightly pointed out that "character not
found" may be a perfectly normal situation for many applications. For a
very few applications, it may be a fatal error. As a utility method
writer, you really don't know how your method is going to be used, so
you don't really know what is "exceptional."

Of course, some conditions are obvious exceptions: out of memory,
network going offline... stuff like that. However a good number of
situations could go either way, depending upon the caller.

So, let's say that the writer of IndexOf were to decide in your favour
and make "character not found" an exception, and provide a ContainsChar
method. I would wager that the vast majority of applications calling
IndexOf do not consider "character not found" a fatal error. So, now
you've forced all of those methods to catch an exception, which makes
the flow of code much trickier and makes those apps harder to maintain,
in addition to being much more costly at runtime. (Yes, I've read Jon's
responses, and he is correct, but catching an exception is still
considerably more costly than testing "if (foo.IndexOf('x') < 0)".)

If said caller wants to avoid the exception, you've made him call an
additional method, which ends up searching the string twice: once to
find out if the character is there, and again to get its index.

All of this to cater to the minority of cases in which not finding what
you're looking for is a fatal error.

So the answer: because it's very much simpler for the majority case,
which is that not finding what you're looking for is not a fatal error.
Which, in the end, means that having IndexOf return -1 is simply a
better design.
 
Gordon Cowie said:
I see. Also, from reading other's replies. The real issue is the "cost"
related to exceptions. If they were free I think it would make for a
much cleaner coding style. Probably even anywhere a special return code
is returned.

No, I don't think so. Not finding a character you've been asked for
just isn't an exceptional condition, IMO. It's almost always a
possibility, and nearly always needs to be handled right then and there
- it would very rarely be appropriate to let an exception bubble up.
Another negative about -1. It forces IndexOf to return an int, when a
uint would be more appropriate. The max length of the string is
effectively cut in half in order to provide space for the possible -1
result.

There are various things already constraining the length of a string:

1) Length is an int property, not a uint one
2) The actual implementation uses the top two bits for flags, so the
actual maximum length of a string is int.MaxValue/2.
3) As the unsigned types aren't CLS-compliant, making String use uint
everywhere would be pretty awkward.

There are plenty of places where I think people use return codes
instead of exceptions inappropriately - but in this case a different
return code is entirely appropriate IMO.
 
So the answer: because it's very much simpler for the majority case,
which is that not finding what you're looking for is not a fatal error.
Which, in the end, means that having IndexOf return -1 is simply a
better design.

Yeah it's too bad about the hidden costs associated with exceptions. I
didn't know this before and now I will be incented to avoid them in some
cases.

I don't believe that returning -1 is a very good design. I'm pretty much
against stashing secret codes in the result.

I think an IndexOf that returns a bool and an out parameter would cater
to those wanting to avoid the exception and still not stooping to -1.

bool String.IndexOf(char Char, out int Index)

But it still doesn't allow you to ever pass the result of an IndexOf
directly into a substring, so you would still have pesky extra variables.
 
In general I agree with you about using "magic return values" to
indicate special situations. However, in this case I think it's the
best design out of the available alternatives.

"Democracy is the worst form of government except for all those others
that have been tried." - Winston Churchill
 
Post some code Willy, I'm not taking your word for it. We all remember this
thread by the way.

--
Warm Regards,
Alvin Bruney [MVP ASP.NET]

[Shameless Author plug]
The Microsoft Office Web Components Black Book with .NET
Now Available @ www.lulu.com/owc
Professional VSTO 2005 - Wrox/Wiley 2006
Blog: http://msmvps.com/blogs/Alvin/
 
The code is found here...

http://www.pobox.com/~skeet/csharp/exceptions.html

Willy.

"Alvin Bruney - ASP.NET MVP" <www.lulu.com/owc> wrote in message
| Post some code Willy, I'm not taking your word for it. We all remember
this
| thread by the way.
|
| --
| Warm Regards,
| Alvin Bruney [MVP ASP.NET]
|
| [Shameless Author plug]
| The Microsoft Office Web Components Black Book with .NET
| Now Available @ www.lulu.com/owc
| Professional VSTO 2005 - Wrox/Wiley 2006
| Blog: http://msmvps.com/blogs/Alvin/
| -------------------------------------------------------
|
|
|
| | > Jon,
| >
| > Note that Exceptions are significantly more expensive in V2, as shown
| here:
| >
| > V1.1
| > Total time taken: 00:00:32.2768033
| > Exceptions per millisecond: 154
| >
| > V2
| > Total time taken: 00:02:59.3157696
| > Exceptions per millisecond: 27
| >
| > that means 37 µsecs. per exception, I would say this is more expensive
as
| > some people think ;-)
| >
| > Willy.
| >
| >
| > | > | > It would also be far more expensive. Exceptions are very expensive
| and
| > | > really, exceptions are for "exceptional" events, things that you
| > normally
| > | > would not have control over.
| > |
| > | Exceptions aren't nearly as expensive as most people think. See
| > | http://www.pobox.com/~skeet/csharp/exceptions.html
| > | (In this particular case the performance penalty could be significant
| > | in many programs, but I don't want you follow the myth of exception
| > | performance :)
| > |
| > | However, they're still wrong in this situation, because it really
isn't
| > | exceptional for a character not to be present in a string - it's
| > | something which is almost always a possibility when you use IndexOf,
in
| > | my experience. It's rarely an error condition which would make you
want
| > | to unwind the stack a significant distance.
| > |
| > | Of course, if Gordon wants to write his own method which throws an
| > | exception instead of returning -1, there's nothing to stop him -
| > | whereas if the method already threw an exception, wrapping it in a
| > | method which returned -1 would incur the performance penalty of the
| > | exception.
| > |
| > | --
| > | Jon Skeet - <[email protected]>
| > | http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
| > | If replying to the group, please do not mail me too
| >
| >
|
|
 
Post some code Willy, I'm not taking your word for it. We all remember this
thread by the way.

As Willy posted, it's the code at the link I posted before. Here are my
results:

1.1:
Total time taken: 00:00:42.7343750
Exceptions per millisecond: 117

2.0:
Total time taken: 00:03:57.1093750
Exceptions per millisecond: 21
 
Back
Top