the c# return statement | | |
The c# *return* statement has been bothering me the past few months.
I don't like the fact that you can have different code paths in a method
and have multiple return statements. To me, it would be more orthogonal
if a method could only have one return statement.
--
W '04 <:> Open Source | | | | re: the c# return statement
John Bailo wrote:
[color=blue]
> The c# *return* statement has been bothering me the past few months.
>
> I don't like the fact that you can have different code paths in a method
> and have multiple return statements. To me, it would be more orthogonal
> if a method could only have one return statement.[/color]
To some extent, I share this sentement :-) I find that
Delphi's/Pascal's 'result' returning mechanism is more elegant, for the
most part. However, I think there are /some/ instances where returning
before the end of a method can produce more straight-forward code.
Example of Delphi's/Pascal's returning mechanism:
function Foo: Boolean;
begin
Result := True;
if FooBar then
Result := GiveMeABoolean();
DoSomethingElse();
end; // return value is value of Result
Of course, this can easily be simulated in C# (but it involves more
statements, generally):
bool Foo()
{
bool result = true;
if (fooBar)
result = GiveMeABoolean();
DoSomethingElse();
return result;
}
Or even better, in this case:
bool Foo()
{
bool result = fooBar ? GiveMeABoolean() : true;
DoSomethingElse();
return result;
}
I guess I don't mind the C-style returning mechanism so much if it's not
abused (in my opinion, according to my own personal style), but find
Delphi's/Pascal's equivalent more elegant in most cases. | | | | re: the c# return statement
John Bailo wrote:
[color=blue]
> The c# *return* statement has been bothering me the past few months.
>
> I don't like the fact that you can have different code paths in a method
> and have multiple return statements. To me, it would be more orthogonal
> if a method could only have one return statement.[/color]
To some extent, I share this sentement :-) I find that
Delphi's/Pascal's 'result' returning mechanism is more elegant, for the
most part. However, I think there are /some/ instances where returning
before the end of a method can produce more straight-forward code.
Example of Delphi's/Pascal's returning mechanism:
function Foo: Boolean;
begin
Result := True;
if FooBar then
Result := GiveMeABoolean();
DoSomethingElse();
end; // return value is value of Result
Of course, this can easily be simulated in C# (but it involves more
statements, generally):
bool Foo()
{
bool result = true;
if (fooBar)
result = GiveMeABoolean();
DoSomethingElse();
return result;
}
Or even better, in this case:
bool Foo()
{
bool result = fooBar ? GiveMeABoolean() : true;
DoSomethingElse();
return result;
}
I guess I don't mind the C-style returning mechanism so much if it's not
abused (in my opinion, according to my own personal style), but find
Delphi's/Pascal's equivalent more elegant in most cases. | | | | re: the c# return statement
John Bailo wrote:
[color=blue]
>
> The c# *return* statement has been bothering me the past few months.
>
> I don't like the fact that you can have different code paths in a method
> and have multiple return statements. To me, it would be more orthogonal
> if a method could only have one return statement.
>[/color]
The C# language is very much based on C++ and Java, multiple return
statements are part of the language.
If you have a problem with multiple return statements, don't use them. | | | | re: the c# return statement
John Bailo wrote:
[color=blue]
>
> The c# *return* statement has been bothering me the past few months.
>
> I don't like the fact that you can have different code paths in a method
> and have multiple return statements. To me, it would be more orthogonal
> if a method could only have one return statement.
>[/color]
The C# language is very much based on C++ and Java, multiple return
statements are part of the language.
If you have a problem with multiple return statements, don't use them. | | | | re: the c# return statement
In article <26a5ed68577bc637437dd356fb3129d2@news.teranews.co m>, John Bailo wrote:[color=blue]
>
> The c# *return* statement has been bothering me the past few months.
>
> I don't like the fact that you can have different code paths in a method
> and have multiple return statements. To me, it would be more orthogonal
> if a method could only have one return statement.[/color]
If you have exceptions, you can have different paths in a method. So unless
you're prepared to do without exceptions and check return codes every time
you tie your shoelaces, you're stuck with the perceived evils of multiple paths.
Cheers,
--
Donovan Rebbechi http://pegasus.rutgers.edu/~elflord/ | | | | re: the c# return statement
In article <26a5ed68577bc637437dd356fb3129d2@news.teranews.co m>, John Bailo wrote:[color=blue]
>
> The c# *return* statement has been bothering me the past few months.
>
> I don't like the fact that you can have different code paths in a method
> and have multiple return statements. To me, it would be more orthogonal
> if a method could only have one return statement.[/color]
If you have exceptions, you can have different paths in a method. So unless
you're prepared to do without exceptions and check return codes every time
you tie your shoelaces, you're stuck with the perceived evils of multiple paths.
Cheers,
--
Donovan Rebbechi http://pegasus.rutgers.edu/~elflord/ | | | | re: the c# return statement
Donovan Rebbechi wrote:
[color=blue]
> In article <26a5ed68577bc637437dd356fb3129d2@news.teranews.co m>, John Bailo wrote:
>[color=green]
>>The c# *return* statement has been bothering me the past few months.
>>
>>I don't like the fact that you can have different code paths in a method
>>and have multiple return statements. To me, it would be more orthogonal
>>if a method could only have one return statement.[/color]
>
> If you have exceptions, you can have different paths in a method. So unless
> you're prepared to do without exceptions and check return codes every time
> you tie your shoelaces, you're stuck with the perceived evils of multiple paths.[/color]
As I say, I agree with JB to some extent in that I don't generally like
returning from methods /before/ the end, with a 'return' statement.
However, I don't feel the same with throwing exceptions since, if one's
using them correctly, one'll only be throwing them in /exceptional/
circumstances anyway, in which case one would have no desire *not* to
return from the method there and then. | | | | re: the c# return statement
Donovan Rebbechi wrote:
[color=blue]
> In article <26a5ed68577bc637437dd356fb3129d2@news.teranews.co m>, John Bailo wrote:
>[color=green]
>>The c# *return* statement has been bothering me the past few months.
>>
>>I don't like the fact that you can have different code paths in a method
>>and have multiple return statements. To me, it would be more orthogonal
>>if a method could only have one return statement.[/color]
>
> If you have exceptions, you can have different paths in a method. So unless
> you're prepared to do without exceptions and check return codes every time
> you tie your shoelaces, you're stuck with the perceived evils of multiple paths.[/color]
As I say, I agree with JB to some extent in that I don't generally like
returning from methods /before/ the end, with a 'return' statement.
However, I don't feel the same with throwing exceptions since, if one's
using them correctly, one'll only be throwing them in /exceptional/
circumstances anyway, in which case one would have no desire *not* to
return from the method there and then. | | | | re: the c# return statement
On Thu, 08 Apr 2004 22:33:22 GMT, John Bailo wrote:
[color=blue]
>
> The c# *return* statement has been bothering me the past few months.
>
> I don't like the fact that you can have different code paths in a method
> and have multiple return statements. To me, it would be more orthogonal
> if a method could only have one return statement.[/color]
You mean "simpler", not "orthogonal".
And it's only simpler if the logic in the function is simple. Once it gets
complex, it makes more sense to return at the point a return is required -
not waiting until later and setting up all kinds of flags and variables to
stash state until you hit the return statement.
Unless you're suggesting that a goto statement to the return would be ok?
--
People in the killfile (and whose posts I won't read) as of 4/8/2004
6:10:03 PM:
Peter Kohlmann, T.Max Devlin. Matt Templeton (scored down) | | | | re: the c# return statement
On Thu, 08 Apr 2004 22:33:22 GMT, John Bailo wrote:
[color=blue]
>
> The c# *return* statement has been bothering me the past few months.
>
> I don't like the fact that you can have different code paths in a method
> and have multiple return statements. To me, it would be more orthogonal
> if a method could only have one return statement.[/color]
You mean "simpler", not "orthogonal".
And it's only simpler if the logic in the function is simple. Once it gets
complex, it makes more sense to return at the point a return is required -
not waiting until later and setting up all kinds of flags and variables to
stash state until you hit the return statement.
Unless you're suggesting that a goto statement to the return would be ok?
--
People in the killfile (and whose posts I won't read) as of 4/8/2004
6:10:03 PM:
Peter Kohlmann, T.Max Devlin. Matt Templeton (scored down) | | | | re: the c# return statement
In article <1mfz4ihxdp3m4$.a483thyh7rrn$@fanatastical.malapro p.net>, Milo T. wrote:[color=blue]
> On Thu, 08 Apr 2004 22:33:22 GMT, John Bailo wrote:
>[color=green]
>>
>> The c# *return* statement has been bothering me the past few months.
>>
>> I don't like the fact that you can have different code paths in a method
>> and have multiple return statements. To me, it would be more orthogonal
>> if a method could only have one return statement.[/color]
>
> You mean "simpler", not "orthogonal".
>
> And it's only simpler if the logic in the function is simple. Once it gets[/color]
To me, very long and complex functions in an OOP language usually indicates
messy coding. One can usually break up the logic into smaller functions.
If the complexity of passing all the local data to these functions is
prohibitive (this is one of the main reasons functions aren't subdivided,
especially in C), it's usually a sign that you need group some of this data
into objects.
[color=blue]
> complex, it makes more sense to return at the point a return is required -
> not waiting until later and setting up all kinds of flags and variables to
> stash state until you hit the return statement.
>
> Unless you're suggesting that a goto statement to the return would be ok?[/color]
Nah. He was thinking of wrapping the function body in a try block.
Cheers,
--
Donovan Rebbechi http://pegasus.rutgers.edu/~elflord/ | | | | re: the c# return statement
In article <1mfz4ihxdp3m4$.a483thyh7rrn$@fanatastical.malapro p.net>, Milo T. wrote:[color=blue]
> On Thu, 08 Apr 2004 22:33:22 GMT, John Bailo wrote:
>[color=green]
>>
>> The c# *return* statement has been bothering me the past few months.
>>
>> I don't like the fact that you can have different code paths in a method
>> and have multiple return statements. To me, it would be more orthogonal
>> if a method could only have one return statement.[/color]
>
> You mean "simpler", not "orthogonal".
>
> And it's only simpler if the logic in the function is simple. Once it gets[/color]
To me, very long and complex functions in an OOP language usually indicates
messy coding. One can usually break up the logic into smaller functions.
If the complexity of passing all the local data to these functions is
prohibitive (this is one of the main reasons functions aren't subdivided,
especially in C), it's usually a sign that you need group some of this data
into objects.
[color=blue]
> complex, it makes more sense to return at the point a return is required -
> not waiting until later and setting up all kinds of flags and variables to
> stash state until you hit the return statement.
>
> Unless you're suggesting that a goto statement to the return would be ok?[/color]
Nah. He was thinking of wrapping the function body in a try block.
Cheers,
--
Donovan Rebbechi http://pegasus.rutgers.edu/~elflord/ | | | | re: the c# return statement
Error BR-549: MS DRM 1.0 rejects the following post from Tom B.:
[color=blue]
> John Bailo wrote:
>
> Example of Delphi's/Pascal's returning mechanism:
>
> function Foo: Boolean;
> begin
> Result := True;
> if FooBar then
> Result := GiveMeABoolean();
> DoSomethingElse();
> end; // return value is value of Result[/color]
Get that ugly crap offa my screen!
<hyperventilates>
I'm no longer using Builder.
I'm no longer using Builder.
I'm no longer using Builder.
--
I tried to read "Dune" but found it a little dry. | | | | re: the c# return statement
Error BR-549: MS DRM 1.0 rejects the following post from Tom B.:
[color=blue]
> John Bailo wrote:
>
> Example of Delphi's/Pascal's returning mechanism:
>
> function Foo: Boolean;
> begin
> Result := True;
> if FooBar then
> Result := GiveMeABoolean();
> DoSomethingElse();
> end; // return value is value of Result[/color]
Get that ugly crap offa my screen!
<hyperventilates>
I'm no longer using Builder.
I'm no longer using Builder.
I'm no longer using Builder.
--
I tried to read "Dune" but found it a little dry. | | | | re: the c# return statement
Error BR-549: MS DRM 1.0 rejects the following post from Donovan Rebbechi:
[color=blue]
> In article <26a5ed68577bc637437dd356fb3129d2@news.teranews.co m>, John Bailo
> wrote:[color=green]
>> I don't like the fact that you can have different code paths in a method and
>> have multiple return statements. To me, it would be more orthogonal if a
>> method could only have one return statement.[/color]
>
> If you have exceptions, you can have different paths in a method. So unless
> you're prepared to do without exceptions and check return codes every time
> you tie your shoelaces, you're stuck with the perceived evils of multiple paths[/color]
Multiple paths can be evil in complete code or code that is dominated by such
paths. I can still remember unrolling some Cosmic FORTRAN code into decent
structured C code.
If a routine fits on the screen, you can pretty much get away with anything.
--
Trust your data to a Linux server or desktop! | | | | re: the c# return statement
Error BR-549: MS DRM 1.0 rejects the following post from Donovan Rebbechi:
[color=blue]
> In article <26a5ed68577bc637437dd356fb3129d2@news.teranews.co m>, John Bailo
> wrote:[color=green]
>> I don't like the fact that you can have different code paths in a method and
>> have multiple return statements. To me, it would be more orthogonal if a
>> method could only have one return statement.[/color]
>
> If you have exceptions, you can have different paths in a method. So unless
> you're prepared to do without exceptions and check return codes every time
> you tie your shoelaces, you're stuck with the perceived evils of multiple paths[/color]
Multiple paths can be evil in complete code or code that is dominated by such
paths. I can still remember unrolling some Cosmic FORTRAN code into decent
structured C code.
If a routine fits on the screen, you can pretty much get away with anything.
--
Trust your data to a Linux server or desktop! | | | | re: the c# return statement
[Removed comp.os.linux.advocacy, which is completely irrelevant here.]
John Bailo <jabailo@earthlink.net> wrote:[color=blue]
> The c# *return* statement has been bothering me the past few months.
>
> I don't like the fact that you can have different code paths in a method
> and have multiple return statements. To me, it would be more orthogonal
> if a method could only have one return statement.[/color]
I disagree. For instance, here's a piece of code I've been using in a
thread on the C# newsgroup recently:
public static bool IsDecimal (string data)
{
bool gotPoint = false;
foreach (char c in data)
{
if (c=='.')
{
if (gotPoint)
{
return false;
}
gotPoint = true;
continue;
}
if (c < '0' || c > '9')
{
return false;
}
}
return true;
}
(This isn't designed to be culture-sensitive, or work with +/- etc -
it's just an example.)
To avoid multiple returns, you end up having to introduce another local
variable, and break out of the loop when you know what the result is
going to be. You could do the breaking part using a while loop instead,
but then the iteration becomes less readable. Either way, you've still
got the extra local variable.
The way I see it, "return" is a powerful way of saying "If you've got
here, you know the result of the method - none of the rest of the
code/state is relevant."
As others have mentioned, if you don't like using it, you don't have to
- you can always put that extra local variable in, along with all the
breaks you need, and then return at the end of the method. No-one's
forcing you to write code like the above - but I'm glad I'm not being
forced *not* to write it, either.
--
Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
If replying to the group, please do not mail me too | | | | re: the c# return statement
[Removed comp.os.linux.advocacy, which is completely irrelevant here.]
John Bailo <jabailo@earthlink.net> wrote:[color=blue]
> The c# *return* statement has been bothering me the past few months.
>
> I don't like the fact that you can have different code paths in a method
> and have multiple return statements. To me, it would be more orthogonal
> if a method could only have one return statement.[/color]
I disagree. For instance, here's a piece of code I've been using in a
thread on the C# newsgroup recently:
public static bool IsDecimal (string data)
{
bool gotPoint = false;
foreach (char c in data)
{
if (c=='.')
{
if (gotPoint)
{
return false;
}
gotPoint = true;
continue;
}
if (c < '0' || c > '9')
{
return false;
}
}
return true;
}
(This isn't designed to be culture-sensitive, or work with +/- etc -
it's just an example.)
To avoid multiple returns, you end up having to introduce another local
variable, and break out of the loop when you know what the result is
going to be. You could do the breaking part using a while loop instead,
but then the iteration becomes less readable. Either way, you've still
got the extra local variable.
The way I see it, "return" is a powerful way of saying "If you've got
here, you know the result of the method - none of the rest of the
code/state is relevant."
As others have mentioned, if you don't like using it, you don't have to
- you can always put that extra local variable in, along with all the
breaks you need, and then return at the end of the method. No-one's
forcing you to write code like the above - but I'm glad I'm not being
forced *not* to write it, either.
--
Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
If replying to the group, please do not mail me too | | | | re: the c# return statement
On Fri, 9 Apr 2004 02:39:43 +0000 (UTC), Donovan Rebbechi wrote:
[color=blue]
> In article <1mfz4ihxdp3m4$.a483thyh7rrn$@fanatastical.malapro p.net>, Milo T. wrote:[color=green]
>> On Thu, 08 Apr 2004 22:33:22 GMT, John Bailo wrote:
>>[color=darkred]
>>>
>>> The c# *return* statement has been bothering me the past few months.
>>>
>>> I don't like the fact that you can have different code paths in a method
>>> and have multiple return statements. To me, it would be more orthogonal
>>> if a method could only have one return statement.[/color]
>>
>> You mean "simpler", not "orthogonal".
>>
>> And it's only simpler if the logic in the function is simple. Once it gets[/color]
>
> To me, very long and complex functions in an OOP language usually indicates
> messy coding. One can usually break up the logic into smaller functions.
> If the complexity of passing all the local data to these functions is
> prohibitive (this is one of the main reasons functions aren't subdivided,
> especially in C), it's usually a sign that you need group some of this data
> into objects.[/color]
Yes, generally I would agree but sometimes breaking things down into
objects is something you don't want to or cannot do (for performance
reasons).
Besides, I find the first clearer than the second personally...
ValueType Function(EnumType a) {
switch(a)
{
case 1:
{
DoSomething();
DoSomethingElse();
return DoSomethingNew();
}
default:
ASSERT("Shouldn't reach here - bad switch value");
// intentional fallthrough
case 2:
{
DoSomething();
DoSomethingElse();
return DoSomethingNew();
}
}
}
ValueType Function(EnumType a) {
ValueType v;
switch(a)
{
case 1:
{
DoSomething();
DoSomethingElse();
v=DoSomethingNew();
break;
}
default:
ASSERT("Shouldn't reach here - bad switch value");
// intentional fallthrough
case 2:
{
DoSomething();
DoSomethingElse();
v=DoSomethingNew();
break;
}
}
return v;
}
--
People in the killfile (and whose posts I won't read) as of 4/9/2004
12:11:27 AM:
Peter Kohlmann, T.Max Devlin. Matt Templeton (scored down) | | | | re: the c# return statement
On Fri, 9 Apr 2004 02:39:43 +0000 (UTC), Donovan Rebbechi wrote:
[color=blue]
> In article <1mfz4ihxdp3m4$.a483thyh7rrn$@fanatastical.malapro p.net>, Milo T. wrote:[color=green]
>> On Thu, 08 Apr 2004 22:33:22 GMT, John Bailo wrote:
>>[color=darkred]
>>>
>>> The c# *return* statement has been bothering me the past few months.
>>>
>>> I don't like the fact that you can have different code paths in a method
>>> and have multiple return statements. To me, it would be more orthogonal
>>> if a method could only have one return statement.[/color]
>>
>> You mean "simpler", not "orthogonal".
>>
>> And it's only simpler if the logic in the function is simple. Once it gets[/color]
>
> To me, very long and complex functions in an OOP language usually indicates
> messy coding. One can usually break up the logic into smaller functions.
> If the complexity of passing all the local data to these functions is
> prohibitive (this is one of the main reasons functions aren't subdivided,
> especially in C), it's usually a sign that you need group some of this data
> into objects.[/color]
Yes, generally I would agree but sometimes breaking things down into
objects is something you don't want to or cannot do (for performance
reasons).
Besides, I find the first clearer than the second personally...
ValueType Function(EnumType a) {
switch(a)
{
case 1:
{
DoSomething();
DoSomethingElse();
return DoSomethingNew();
}
default:
ASSERT("Shouldn't reach here - bad switch value");
// intentional fallthrough
case 2:
{
DoSomething();
DoSomethingElse();
return DoSomethingNew();
}
}
}
ValueType Function(EnumType a) {
ValueType v;
switch(a)
{
case 1:
{
DoSomething();
DoSomethingElse();
v=DoSomethingNew();
break;
}
default:
ASSERT("Shouldn't reach here - bad switch value");
// intentional fallthrough
case 2:
{
DoSomething();
DoSomethingElse();
v=DoSomethingNew();
break;
}
}
return v;
}
--
People in the killfile (and whose posts I won't read) as of 4/9/2004
12:11:27 AM:
Peter Kohlmann, T.Max Devlin. Matt Templeton (scored down) | | | | re: the c# return statement
Milo T. wrote:
[color=blue]
> On Fri, 9 Apr 2004 02:39:43 +0000 (UTC), Donovan Rebbechi wrote:
>[color=green]
>> In article <1mfz4ihxdp3m4$.a483thyh7rrn$@fanatastical.malapro p.net>, Milo
>> T. wrote:[color=darkred]
>>> On Thu, 08 Apr 2004 22:33:22 GMT, John Bailo wrote:
>>>
>>>>
>>>> The c# *return* statement has been bothering me the past few months.
>>>>
>>>> I don't like the fact that you can have different code paths in a
>>>> method
>>>> and have multiple return statements. To me, it would be more
>>>> orthogonal if a method could only have one return statement.
>>>
>>> You mean "simpler", not "orthogonal".
>>>
>>> And it's only simpler if the logic in the function is simple. Once it
>>> gets[/color]
>>
>> To me, very long and complex functions in an OOP language usually
>> indicates
>> messy coding. One can usually break up the logic into smaller functions.
>> If the complexity of passing all the local data to these functions is
>> prohibitive (this is one of the main reasons functions aren't subdivided,
>> especially in C), it's usually a sign that you need group some of this
>> data into objects.[/color]
>
> Yes, generally I would agree but sometimes breaking things down into
> objects is something you don't want to or cannot do (for performance
> reasons).
>
> Besides, I find the first clearer than the second personally...
>
> ValueType Function(EnumType a) {
>
> switch(a)
> {
> case 1:
> {
> DoSomething();
> DoSomethingElse();
> return DoSomethingNew();
> }
> default:
> ASSERT("Shouldn't reach here - bad switch value");
> // intentional fallthrough
> case 2:
> {
> DoSomething();
> DoSomethingElse();
> return DoSomethingNew();
> }
> }
>
> }
>
> ValueType Function(EnumType a) {
>
> ValueType v;
>
> switch(a)
> {
> case 1:
> {
> DoSomething();
> DoSomethingElse();
> v=DoSomethingNew();
> break;
> }
> default:
> ASSERT("Shouldn't reach here - bad switch value");
> // intentional fallthrough
> case 2:
> {
> DoSomething();
> DoSomethingElse();
> v=DoSomethingNew();
> break;
> }
> }
>
> return v;
>
> }
>
>[/color]
See, that's exactly what I don't like -- having conditional multiple
returns.
What I propose is the following for a method declaration, which is normally:
public type funname(type param)
would now be
public @returnVar=null type funname(type param)
in this case, @returnVar is declared as part of the general declaration and
so is inherent in the the method, as well as it's default value. thus
funname can never not return a value or have an ambiguous code path. yet,
within the method the value of @returnVal can be continuously redefined.
--
W '04 <:> Open | | | | re: the c# return statement
Milo T. wrote:
[color=blue]
> On Fri, 9 Apr 2004 02:39:43 +0000 (UTC), Donovan Rebbechi wrote:
>[color=green]
>> In article <1mfz4ihxdp3m4$.a483thyh7rrn$@fanatastical.malapro p.net>, Milo
>> T. wrote:[color=darkred]
>>> On Thu, 08 Apr 2004 22:33:22 GMT, John Bailo wrote:
>>>
>>>>
>>>> The c# *return* statement has been bothering me the past few months.
>>>>
>>>> I don't like the fact that you can have different code paths in a
>>>> method
>>>> and have multiple return statements. To me, it would be more
>>>> orthogonal if a method could only have one return statement.
>>>
>>> You mean "simpler", not "orthogonal".
>>>
>>> And it's only simpler if the logic in the function is simple. Once it
>>> gets[/color]
>>
>> To me, very long and complex functions in an OOP language usually
>> indicates
>> messy coding. One can usually break up the logic into smaller functions.
>> If the complexity of passing all the local data to these functions is
>> prohibitive (this is one of the main reasons functions aren't subdivided,
>> especially in C), it's usually a sign that you need group some of this
>> data into objects.[/color]
>
> Yes, generally I would agree but sometimes breaking things down into
> objects is something you don't want to or cannot do (for performance
> reasons).
>
> Besides, I find the first clearer than the second personally...
>
> ValueType Function(EnumType a) {
>
> switch(a)
> {
> case 1:
> {
> DoSomething();
> DoSomethingElse();
> return DoSomethingNew();
> }
> default:
> ASSERT("Shouldn't reach here - bad switch value");
> // intentional fallthrough
> case 2:
> {
> DoSomething();
> DoSomethingElse();
> return DoSomethingNew();
> }
> }
>
> }
>
> ValueType Function(EnumType a) {
>
> ValueType v;
>
> switch(a)
> {
> case 1:
> {
> DoSomething();
> DoSomethingElse();
> v=DoSomethingNew();
> break;
> }
> default:
> ASSERT("Shouldn't reach here - bad switch value");
> // intentional fallthrough
> case 2:
> {
> DoSomething();
> DoSomethingElse();
> v=DoSomethingNew();
> break;
> }
> }
>
> return v;
>
> }
>
>[/color]
See, that's exactly what I don't like -- having conditional multiple
returns.
What I propose is the following for a method declaration, which is normally:
public type funname(type param)
would now be
public @returnVar=null type funname(type param)
in this case, @returnVar is declared as part of the general declaration and
so is inherent in the the method, as well as it's default value. thus
funname can never not return a value or have an ambiguous code path. yet,
within the method the value of @returnVal can be continuously redefined.
--
W '04 <:> Open | | | | re: the c# return statement
Tom B. wrote:
[color=blue]
> Or even better, in this case:
>
> bool Foo()
> {
> bool result = fooBar ? GiveMeABoolean() : true;
> DoSomethingElse();
> return result;
> }[/color]
I like:
bool Foo()
{
bool result = !foobar || GiveMeABoolean();
DoSomethingElse();
return result;
}
ACK on the C/Pascal style thing.
Regards,
Whelp | | | | re: the c# return statement
Tom B. wrote:
[color=blue]
> Or even better, in this case:
>
> bool Foo()
> {
> bool result = fooBar ? GiveMeABoolean() : true;
> DoSomethingElse();
> return result;
> }[/color]
I like:
bool Foo()
{
bool result = !foobar || GiveMeABoolean();
DoSomethingElse();
return result;
}
ACK on the C/Pascal style thing.
Regards,
Whelp | | | | re: the c# return statement
> The c# *return* statement has been bothering me the past few months.[color=blue]
>
> I don't like the fact that you can have different code paths in a method
> and have multiple return statements. To me, it would be more orthogonal
> if a method could only have one return statement.[/color]
even in pascal/delphi you can prematurely exit a method which often is
useful.
--
cody
[Freeware, Games and Humor] www.deutronium.de.vu || www.deutronium.tk | | | | re: the c# return statement
> The c# *return* statement has been bothering me the past few months.[color=blue]
>
> I don't like the fact that you can have different code paths in a method
> and have multiple return statements. To me, it would be more orthogonal
> if a method could only have one return statement.[/color]
even in pascal/delphi you can prematurely exit a method which often is
useful.
--
cody
[Freeware, Games and Humor] www.deutronium.de.vu || www.deutronium.tk | | | | re: the c# return statement
In article <1wz6yoaop9nv0.1oqbftjh5dyny$@fanatastical.malapro p.net>, Milo T. wrote:
[color=blue]
> Yes, generally I would agree but sometimes breaking things down into
> objects is something you don't want to or cannot do (for performance
> reasons).[/color]
Seriously ? I mean, can't a switch be performed via table lookup as opposed to
an if/then/else ?
switch is one of my least favourite constructs in code. It can always be
replaced with table lookup or polymorphism. Both of these would better
address the "bad switch value" issue.
Cheers,
--
Donovan Rebbechi http://pegasus.rutgers.edu/~elflord/ | | | | re: the c# return statement
In article <1wz6yoaop9nv0.1oqbftjh5dyny$@fanatastical.malapro p.net>, Milo T. wrote:
[color=blue]
> Yes, generally I would agree but sometimes breaking things down into
> objects is something you don't want to or cannot do (for performance
> reasons).[/color]
Seriously ? I mean, can't a switch be performed via table lookup as opposed to
an if/then/else ?
switch is one of my least favourite constructs in code. It can always be
replaced with table lookup or polymorphism. Both of these would better
address the "bad switch value" issue.
Cheers,
--
Donovan Rebbechi http://pegasus.rutgers.edu/~elflord/ | | | | re: the c# return statement
On Fri, 9 Apr 2004 13:57:04 +0000 (UTC), Donovan Rebbechi wrote:
[color=blue]
> In article <1wz6yoaop9nv0.1oqbftjh5dyny$@fanatastical.malapro p.net>, Milo T. wrote:
>[color=green]
>> Yes, generally I would agree but sometimes breaking things down into
>> objects is something you don't want to or cannot do (for performance
>> reasons).[/color]
>
> Seriously ? I mean, can't a switch be performed via table lookup as opposed to
> an if/then/else ?
>
> switch is one of my least favourite constructs in code. It can always be
> replaced with table lookup or polymorphism. Both of these would better
> address the "bad switch value" issue.[/color]
Well, any good compiler will take a switch statement and turn it into a
lookup table for you.
As for polymorphism - in C#, I guess there's no real impact. In C++, you're
now carrying around a vtable.
*shrugs* All depends on what you're optimizing for, I guess.
--
People in the killfile (and whose posts I won't read) as of 4/9/2004
9:13:52 AM:
Peter Kohlmann, T.Max Devlin. Matt Templeton (scored down) | | | | re: the c# return statement
On Fri, 9 Apr 2004 13:57:04 +0000 (UTC), Donovan Rebbechi wrote:[color=blue]
> switch is one of my least favourite constructs in code. It can always be
> replaced with table lookup or polymorphism. Both of these would better
> address the "bad switch value" issue.[/color]
Oh, and the other reason to use a switch:
Object* ConstructMeAnObject(unsigned short idvalue)
{
switch (idvalue)
{
case Square:
return new Square();
case Circle:
return new Circle();
case Pentagon:
return new Pentagon();
case Sheep:
return new Sheep();
}
}
.... which is the kind of code that gets important pretty quickly once you
start worry about serialization of data over a network, or to and from
files.
Sure, you can craft your own table of values and function pointers, but
it's not as readable - and will never be as readable until C++/C/C# gets
better support for table-driving data structures.
Although I'd find better table-driven support a bit of a boon right now. At
the moment I have to tie a UI control to an internal lookup ID, to a member
on a network serialized structure, to a scaling ID, to an output data
structure.
Sure would be nice to be able to just fill in a table, and get that to
spill out all of the interconnections instead of having to hack them in
manually.
--
People in the killfile (and whose posts I won't read) as of 4/9/2004
9:21:11 AM:
Peter Kohlmann, T.Max Devlin. Matt Templeton (scored down) | | | | re: the c# return statement
On Fri, 9 Apr 2004 13:57:04 +0000 (UTC), Donovan Rebbechi wrote:
[color=blue]
> In article <1wz6yoaop9nv0.1oqbftjh5dyny$@fanatastical.malapro p.net>, Milo T. wrote:
>[color=green]
>> Yes, generally I would agree but sometimes breaking things down into
>> objects is something you don't want to or cannot do (for performance
>> reasons).[/color]
>
> Seriously ? I mean, can't a switch be performed via table lookup as opposed to
> an if/then/else ?
>
> switch is one of my least favourite constructs in code. It can always be
> replaced with table lookup or polymorphism. Both of these would better
> address the "bad switch value" issue.[/color]
Well, any good compiler will take a switch statement and turn it into a
lookup table for you.
As for polymorphism - in C#, I guess there's no real impact. In C++, you're
now carrying around a vtable.
*shrugs* All depends on what you're optimizing for, I guess.
--
People in the killfile (and whose posts I won't read) as of 4/9/2004
9:13:52 AM:
Peter Kohlmann, T.Max Devlin. Matt Templeton (scored down) | | | | re: the c# return statement
On Fri, 9 Apr 2004 13:57:04 +0000 (UTC), Donovan Rebbechi wrote:[color=blue]
> switch is one of my least favourite constructs in code. It can always be
> replaced with table lookup or polymorphism. Both of these would better
> address the "bad switch value" issue.[/color]
Oh, and the other reason to use a switch:
Object* ConstructMeAnObject(unsigned short idvalue)
{
switch (idvalue)
{
case Square:
return new Square();
case Circle:
return new Circle();
case Pentagon:
return new Pentagon();
case Sheep:
return new Sheep();
}
}
.... which is the kind of code that gets important pretty quickly once you
start worry about serialization of data over a network, or to and from
files.
Sure, you can craft your own table of values and function pointers, but
it's not as readable - and will never be as readable until C++/C/C# gets
better support for table-driving data structures.
Although I'd find better table-driven support a bit of a boon right now. At
the moment I have to tie a UI control to an internal lookup ID, to a member
on a network serialized structure, to a scaling ID, to an output data
structure.
Sure would be nice to be able to just fill in a table, and get that to
spill out all of the interconnections instead of having to hack them in
manually.
--
People in the killfile (and whose posts I won't read) as of 4/9/2004
9:21:11 AM:
Peter Kohlmann, T.Max Devlin. Matt Templeton (scored down) | | | | re: the c# return statement
In article <1fg2hna77y681.stwq19v2z9b0$@fanatastical.malaprop .net>, Milo T. wrote:[color=blue]
> On Fri, 9 Apr 2004 13:57:04 +0000 (UTC), Donovan Rebbechi wrote:[color=green]
>> switch is one of my least favourite constructs in code. It can always be
>> replaced with table lookup or polymorphism. Both of these would better
>> address the "bad switch value" issue.[/color]
>
> Oh, and the other reason to use a switch:
>
> Object* ConstructMeAnObject(unsigned short idvalue)
> {
> switch (idvalue)
> {
> case Square:
> return new Square();
> case Circle:
> return new Circle();
> case Pentagon:
> return new Pentagon();
> case Sheep:
> return new Sheep();
> }
> }
>
> ... which is the kind of code that gets important pretty quickly once you
> start worry about serialization of data over a network, or to and from
> files.[/color]
Better, because this kind of code usually consolidates the switch in the one
place. Note that this sort of construction doesn't come up that many times
in the same program, because you only have one of these for each family of
classes.
[color=blue]
> Sure, you can craft your own table of values and function pointers, but
> it's not as readable - and will never be as readable until C++/C/C# gets
> better support for table-driving data structures.[/color]
see std::map. The clean way to implement the above in c++ is to have a
std::map<int, Object*> and use a clone() method to get your prototype.
The translation unit that defines the subclass in question takes care of
inserting the instance into the map.
e.g.
template <class T> insert_into_map {
insert_into_map(std::string s) {
T* x = new T;
the_map().insert(s,x); // the_map is the global table
}
};
class Circle {
....
};
namespace {
insert_into_map <Circle> x("Circle);
};
Object* make_object(const std::string & id){
std::map<std::string,Object*>::iterator it = the_map().find(id);
if (it==the_map().end())
return NULL;
else return it->second->clone();
}
This also has the advantage that you can populate the map at runtime if you
dynamically load the code that defines circle (so you get runtime plugin
support)
Cheers,
--
Donovan Rebbechi http://pegasus.rutgers.edu/~elflord/ | | | | re: the c# return statement
In article <1fg2hna77y681.stwq19v2z9b0$@fanatastical.malaprop .net>, Milo T. wrote:[color=blue]
> On Fri, 9 Apr 2004 13:57:04 +0000 (UTC), Donovan Rebbechi wrote:[color=green]
>> switch is one of my least favourite constructs in code. It can always be
>> replaced with table lookup or polymorphism. Both of these would better
>> address the "bad switch value" issue.[/color]
>
> Oh, and the other reason to use a switch:
>
> Object* ConstructMeAnObject(unsigned short idvalue)
> {
> switch (idvalue)
> {
> case Square:
> return new Square();
> case Circle:
> return new Circle();
> case Pentagon:
> return new Pentagon();
> case Sheep:
> return new Sheep();
> }
> }
>
> ... which is the kind of code that gets important pretty quickly once you
> start worry about serialization of data over a network, or to and from
> files.[/color]
Better, because this kind of code usually consolidates the switch in the one
place. Note that this sort of construction doesn't come up that many times
in the same program, because you only have one of these for each family of
classes.
[color=blue]
> Sure, you can craft your own table of values and function pointers, but
> it's not as readable - and will never be as readable until C++/C/C# gets
> better support for table-driving data structures.[/color]
see std::map. The clean way to implement the above in c++ is to have a
std::map<int, Object*> and use a clone() method to get your prototype.
The translation unit that defines the subclass in question takes care of
inserting the instance into the map.
e.g.
template <class T> insert_into_map {
insert_into_map(std::string s) {
T* x = new T;
the_map().insert(s,x); // the_map is the global table
}
};
class Circle {
....
};
namespace {
insert_into_map <Circle> x("Circle);
};
Object* make_object(const std::string & id){
std::map<std::string,Object*>::iterator it = the_map().find(id);
if (it==the_map().end())
return NULL;
else return it->second->clone();
}
This also has the advantage that you can populate the map at runtime if you
dynamically load the code that defines circle (so you get runtime plugin
support)
Cheers,
--
Donovan Rebbechi http://pegasus.rutgers.edu/~elflord/ | | | | re: the c# return statement
On Fri, 09 Apr 2004 16:26:05 +0000, Milo T. wrote:
[color=blue]
> On Fri, 9 Apr 2004 13:57:04 +0000 (UTC), Donovan Rebbechi wrote:[color=green]
>> switch is one of my least favourite constructs in code. It can always be
>> replaced with table lookup or polymorphism. Both of these would better
>> address the "bad switch value" issue.[/color]
>
> Oh, and the other reason to use a switch:
>
> Object* ConstructMeAnObject(unsigned short idvalue)
> {
> switch (idvalue)
> {
> case Square:
> return new Square();
> case Circle:
> return new Circle();
> case Pentagon:
> return new Pentagon();
> case Sheep:
> return new Sheep();
> }
> }
>
> ... which is the kind of code that gets important pretty quickly once you
> start worry about serialization of data over a network, or to and from
> files.
>
> Sure, you can craft your own table of values and function pointers, but
> it's not as readable - and will never be as readable until C++/C/C# gets
> better support for table-driving data structures.
>
> Although I'd find better table-driven support a bit of a boon right now. At
> the moment I have to tie a UI control to an internal lookup ID, to a member
> on a network serialized structure, to a scaling ID, to an output data
> structure.
>
> Sure would be nice to be able to just fill in a table, and get that to
> spill out all of the interconnections instead of having to hack them in
> manually.[/color]
This is a good one. If the idvalue is other than Square, Circle, Pentagon
or Sheep, then you have no return. To that I have to say that one of the
tennits of good coding, states that I cannot trust an object where I do
not control the memory in which it resides. The returned object is not in
the callers control and hence may be invalid. This may be fast but it is
very dangerous. There are far better patterns were reliability is required
and isn't that all the time.
Ian | | | | re: the c# return statement
On Fri, 09 Apr 2004 16:26:05 +0000, Milo T. wrote:
[color=blue]
> On Fri, 9 Apr 2004 13:57:04 +0000 (UTC), Donovan Rebbechi wrote:[color=green]
>> switch is one of my least favourite constructs in code. It can always be
>> replaced with table lookup or polymorphism. Both of these would better
>> address the "bad switch value" issue.[/color]
>
> Oh, and the other reason to use a switch:
>
> Object* ConstructMeAnObject(unsigned short idvalue)
> {
> switch (idvalue)
> {
> case Square:
> return new Square();
> case Circle:
> return new Circle();
> case Pentagon:
> return new Pentagon();
> case Sheep:
> return new Sheep();
> }
> }
>
> ... which is the kind of code that gets important pretty quickly once you
> start worry about serialization of data over a network, or to and from
> files.
>
> Sure, you can craft your own table of values and function pointers, but
> it's not as readable - and will never be as readable until C++/C/C# gets
> better support for table-driving data structures.
>
> Although I'd find better table-driven support a bit of a boon right now. At
> the moment I have to tie a UI control to an internal lookup ID, to a member
> on a network serialized structure, to a scaling ID, to an output data
> structure.
>
> Sure would be nice to be able to just fill in a table, and get that to
> spill out all of the interconnections instead of having to hack them in
> manually.[/color]
This is a good one. If the idvalue is other than Square, Circle, Pentagon
or Sheep, then you have no return. To that I have to say that one of the
tennits of good coding, states that I cannot trust an object where I do
not control the memory in which it resides. The returned object is not in
the callers control and hence may be invalid. This may be fast but it is
very dangerous. There are far better patterns were reliability is required
and isn't that all the time.
Ian | | | | re: the c# return statement
"Donovan Rebbechi" <abuse@aol.com> wrote in message
news:slrnc7ds5b.lpq.abuse@panix2.panix.com...[color=blue]
> In article <1fg2hna77y681.stwq19v2z9b0$@fanatastical.malaprop .net>, Milo
> T. wrote:[/color]
[color=blue][color=green]
>> Sure, you can craft your own table of values and function pointers, but
>> it's not as readable - and will never be as readable until C++/C/C# gets
>> better support for table-driving data structures.[/color]
>
> see std::map. The clean way to implement the above in c++ is to have a
> std::map<int, Object*> and use a clone() method to get your prototype.
> The translation unit that defines the subclass in question takes care of
> inserting the instance into the map.
>
> e.g.
>
> template <class T> insert_into_map {
> insert_into_map(std::string s) {
> T* x = new T;
> the_map().insert(s,x); // the_map is the global table
> }
> };
>
> class Circle {
>
> ....
>
> };
> namespace {
> insert_into_map <Circle> x("Circle);
> };
>
> Object* make_object(const std::string & id){
> std::map<std::string,Object*>::iterator it = the_map().find(id);
> if (it==the_map().end())
> return NULL;
> else return it->second->clone();
> }
>
> This also has the advantage that you can populate the map at runtime if
> you
> dynamically load the code that defines circle (so you get runtime plugin
> support)[/color]
IMHO, anything solution to a problem so simple as choosing a path from 3-5
static options that uses templates and complex logic is really overthinking
the problem. The resultant code is going to be slower, the time to fix bugs
is going to increase, and the code understandability goes down.
Also, in the case you want dynamic lookups, I would recommend a factory
approach over a cloning approach. That not only allows you dynamic lookups,
but also allows dynamic parameters and saves you from having to design a
class so it can be instantiated without actually doing its work.
The equivilent C# code(using generics partially) would be...
public interface IObjectFactory
{
object CreateObject(object[] arguments);
}
public class MapLookupClass
{
Dictionary<short,IObjectFactory> dict = new Dictionary<short,object>();
public vpod InsertIntoMap(short id, IObjectFactory factory)
{
dict.Add(id,objectFactory);
}
public MakeObject(short id, object[] arguments)
{
IObjectFactory fac;
fac = dict[id];
return fac.CreateObject(arguments);
}
}
It is more readable than the C++ approach, IMHO, but it is still
considerably less readable than a simple switch.
It doesn't strive to achieve automatic registration as yours does, although
I think that is possible I don't have a compiler on hand to write something
to test it and get the syntax right ATM. I would probably use attributes and
reflection anyway.[color=blue]
>
>
> Cheers,
> --
> Donovan Rebbechi
> http://pegasus.rutgers.edu/~elflord/[/color] | | | | re: the c# return statement
"Donovan Rebbechi" <abuse@aol.com> wrote in message
news:slrnc7ds5b.lpq.abuse@panix2.panix.com...[color=blue]
> In article <1fg2hna77y681.stwq19v2z9b0$@fanatastical.malaprop .net>, Milo
> T. wrote:[/color]
[color=blue][color=green]
>> Sure, you can craft your own table of values and function pointers, but
>> it's not as readable - and will never be as readable until C++/C/C# gets
>> better support for table-driving data structures.[/color]
>
> see std::map. The clean way to implement the above in c++ is to have a
> std::map<int, Object*> and use a clone() method to get your prototype.
> The translation unit that defines the subclass in question takes care of
> inserting the instance into the map.
>
> e.g.
>
> template <class T> insert_into_map {
> insert_into_map(std::string s) {
> T* x = new T;
> the_map().insert(s,x); // the_map is the global table
> }
> };
>
> class Circle {
>
> ....
>
> };
> namespace {
> insert_into_map <Circle> x("Circle);
> };
>
> Object* make_object(const std::string & id){
> std::map<std::string,Object*>::iterator it = the_map().find(id);
> if (it==the_map().end())
> return NULL;
> else return it->second->clone();
> }
>
> This also has the advantage that you can populate the map at runtime if
> you
> dynamically load the code that defines circle (so you get runtime plugin
> support)[/color]
IMHO, anything solution to a problem so simple as choosing a path from 3-5
static options that uses templates and complex logic is really overthinking
the problem. The resultant code is going to be slower, the time to fix bugs
is going to increase, and the code understandability goes down.
Also, in the case you want dynamic lookups, I would recommend a factory
approach over a cloning approach. That not only allows you dynamic lookups,
but also allows dynamic parameters and saves you from having to design a
class so it can be instantiated without actually doing its work.
The equivilent C# code(using generics partially) would be...
public interface IObjectFactory
{
object CreateObject(object[] arguments);
}
public class MapLookupClass
{
Dictionary<short,IObjectFactory> dict = new Dictionary<short,object>();
public vpod InsertIntoMap(short id, IObjectFactory factory)
{
dict.Add(id,objectFactory);
}
public MakeObject(short id, object[] arguments)
{
IObjectFactory fac;
fac = dict[id];
return fac.CreateObject(arguments);
}
}
It is more readable than the C++ approach, IMHO, but it is still
considerably less readable than a simple switch.
It doesn't strive to achieve automatic registration as yours does, although
I think that is possible I don't have a compiler on hand to write something
to test it and get the syntax right ATM. I would probably use attributes and
reflection anyway.[color=blue]
>
>
> Cheers,
> --
> Donovan Rebbechi
> http://pegasus.rutgers.edu/~elflord/[/color] | | | | re: the c# return statement
On Fri, 09 Apr 2004 21:03:10 +0200, Ian Hilliard wrote:
[color=blue]
> On Fri, 09 Apr 2004 16:26:05 +0000, Milo T. wrote:
>[color=green]
>> On Fri, 9 Apr 2004 13:57:04 +0000 (UTC), Donovan Rebbechi wrote:[color=darkred]
>>> switch is one of my least favourite constructs in code. It can always be
>>> replaced with table lookup or polymorphism. Both of these would better
>>> address the "bad switch value" issue.[/color]
>>
>> Oh, and the other reason to use a switch:
>>
>> Object* ConstructMeAnObject(unsigned short idvalue)
>> {
>> switch (idvalue)
>> {
>> case Square:
>> return new Square();
>> case Circle:
>> return new Circle();
>> case Pentagon:
>> return new Pentagon();
>> case Sheep:
>> return new Sheep();
>> }
>> }
>>
>> ... which is the kind of code that gets important pretty quickly once you
>> start worry about serialization of data over a network, or to and from
>> files.
>>
>> Sure, you can craft your own table of values and function pointers, but
>> it's not as readable - and will never be as readable until C++/C/C# gets
>> better support for table-driving data structures.
>>
>> Although I'd find better table-driven support a bit of a boon right now. At
>> the moment I have to tie a UI control to an internal lookup ID, to a member
>> on a network serialized structure, to a scaling ID, to an output data
>> structure.
>>
>> Sure would be nice to be able to just fill in a table, and get that to
>> spill out all of the interconnections instead of having to hack them in
>> manually.[/color]
>
> This is a good one. If the idvalue is other than Square, Circle, Pentagon
> or Sheep, then you have no return. To that I have to say that one of the
> tennits of good coding, states that I cannot trust an object where I do
> not control the memory in which it resides. The returned object is not in
> the callers control and hence may be invalid. This may be fast but it is
> very dangerous. There are far better patterns were reliability is required
> and isn't that all the time.[/color]
Well, I wasn't trying to write solid and robust code, I was trying to spend
5 seconds coming up with examples where switch statements would be useful.
However, I appreciate your willingness in becoming a code reviewer for me,
though I would like to point out that the correct way to solve the above
problem is merely to add a default: return NULL; or default: throw
exception("Unknown object type in deserialization"); to the bottom of the
statement.
In future, I will remember to provide you with a fully compiled, zipped up
source tree with a make file instead of just a quick off the cuff example.
By the way, you missed the fact that I was missing a main() function as
well.
--
People in the killfile (and whose posts I won't read) as of 4/9/2004
1:52:27 PM:
Peter Kohlmann, T.Max Devlin. Matt Templeton (scored down) | | | | re: the c# return statement
On Fri, 9 Apr 2004 18:51:24 +0000 (UTC), Donovan Rebbechi wrote:
[color=blue]
> In article <1fg2hna77y681.stwq19v2z9b0$@fanatastical.malaprop .net>, Milo T. wrote:[color=green]
>> On Fri, 9 Apr 2004 13:57:04 +0000 (UTC), Donovan Rebbechi wrote:[color=darkred]
>>> switch is one of my least favourite constructs in code. It can always be
>>> replaced with table lookup or polymorphism. Both of these would better
>>> address the "bad switch value" issue.[/color]
>>
>> Oh, and the other reason to use a switch:
>>
>> Object* ConstructMeAnObject(unsigned short idvalue)
>> {
>> switch (idvalue)
>> {
>> case Square:
>> return new Square();
>> case Circle:
>> return new Circle();
>> case Pentagon:
>> return new Pentagon();
>> case Sheep:
>> return new Sheep();
>> }
>> }
>>
>> ... which is the kind of code that gets important pretty quickly once you
>> start worry about serialization of data over a network, or to and from
>> files.[/color]
>
> Better, because this kind of code usually consolidates the switch in the one
> place. Note that this sort of construction doesn't come up that many times
> in the same program, because you only have one of these for each family of
> classes.
>[color=green]
>> Sure, you can craft your own table of values and function pointers, but
>> it's not as readable - and will never be as readable until C++/C/C# gets
>> better support for table-driving data structures.[/color]
>
> see std::map. The clean way to implement the above in c++ is to have a
> std::map<int, Object*> and use a clone() method to get your prototype.
> The translation unit that defines the subclass in question takes care of
> inserting the instance into the map.
>
> e.g.
>
> template <class T> insert_into_map {
> insert_into_map(std::string s) {
> T* x = new T;
> the_map().insert(s,x); // the_map is the global table
> }
> };
>
> class Circle {
>
> ....
>
> };
> namespace {
> insert_into_map <Circle> x("Circle);
> };
>
> Object* make_object(const std::string & id){
> std::map<std::string,Object*>::iterator it = the_map().find(id);
> if (it==the_map().end())
> return NULL;
> else return it->second->clone();
> }
>
> This also has the advantage that you can populate the map at runtime if you
> dynamically load the code that defines circle (so you get runtime plugin
> support)[/color]
It also has the disadvantage that you're using variable length strings as
your key for your map, you're using a tree for the lookup instead of a
hashtable or a trie (woe betide if you ever have to use this in a fast
fashion), and it adds to your initialization time. Not to mention the
potential memory cost of all of these Object's that you need to clone
later.
There's no point making code extensible unless you plan to extend it. Wrap
it in a function, and you can replace with an extensible version later.
Most cases, however, will survive perfectly well with the much faster
switch() statement.
--
People in the killfile (and whose posts I won't read) as of 4/9/2004
1:56:00 PM:
Peter Kohlmann, T.Max Devlin. Matt Templeton (scored down) | | | | re: the c# return statement
On Fri, 09 Apr 2004 21:03:10 +0200, Ian Hilliard wrote:
[color=blue]
> On Fri, 09 Apr 2004 16:26:05 +0000, Milo T. wrote:
>[color=green]
>> On Fri, 9 Apr 2004 13:57:04 +0000 (UTC), Donovan Rebbechi wrote:[color=darkred]
>>> switch is one of my least favourite constructs in code. It can always be
>>> replaced with table lookup or polymorphism. Both of these would better
>>> address the "bad switch value" issue.[/color]
>>
>> Oh, and the other reason to use a switch:
>>
>> Object* ConstructMeAnObject(unsigned short idvalue)
>> {
>> switch (idvalue)
>> {
>> case Square:
>> return new Square();
>> case Circle:
>> return new Circle();
>> case Pentagon:
>> return new Pentagon();
>> case Sheep:
>> return new Sheep();
>> }
>> }
>>
>> ... which is the kind of code that gets important pretty quickly once you
>> start worry about serialization of data over a network, or to and from
>> files.
>>
>> Sure, you can craft your own table of values and function pointers, but
>> it's not as readable - and will never be as readable until C++/C/C# gets
>> better support for table-driving data structures.
>>
>> Although I'd find better table-driven support a bit of a boon right now. At
>> the moment I have to tie a UI control to an internal lookup ID, to a member
>> on a network serialized structure, to a scaling ID, to an output data
>> structure.
>>
>> Sure would be nice to be able to just fill in a table, and get that to
>> spill out all of the interconnections instead of having to hack them in
>> manually.[/color]
>
> This is a good one. If the idvalue is other than Square, Circle, Pentagon
> or Sheep, then you have no return. To that I have to say that one of the
> tennits of good coding, states that I cannot trust an object where I do
> not control the memory in which it resides. The returned object is not in
> the callers control and hence may be invalid. This may be fast but it is
> very dangerous. There are far better patterns were reliability is required
> and isn't that all the time.[/color]
Well, I wasn't trying to write solid and robust code, I was trying to spend
5 seconds coming up with examples where switch statements would be useful.
However, I appreciate your willingness in becoming a code reviewer for me,
though I would like to point out that the correct way to solve the above
problem is merely to add a default: return NULL; or default: throw
exception("Unknown object type in deserialization"); to the bottom of the
statement.
In future, I will remember to provide you with a fully compiled, zipped up
source tree with a make file instead of just a quick off the cuff example.
By the way, you missed the fact that I was missing a main() function as
well.
--
People in the killfile (and whose posts I won't read) as of 4/9/2004
1:52:27 PM:
Peter Kohlmann, T.Max Devlin. Matt Templeton (scored down) | | | | re: the c# return statement
On Fri, 9 Apr 2004 18:51:24 +0000 (UTC), Donovan Rebbechi wrote:
[color=blue]
> In article <1fg2hna77y681.stwq19v2z9b0$@fanatastical.malaprop .net>, Milo T. wrote:[color=green]
>> On Fri, 9 Apr 2004 13:57:04 +0000 (UTC), Donovan Rebbechi wrote:[color=darkred]
>>> switch is one of my least favourite constructs in code. It can always be
>>> replaced with table lookup or polymorphism. Both of these would better
>>> address the "bad switch value" issue.[/color]
>>
>> Oh, and the other reason to use a switch:
>>
>> Object* ConstructMeAnObject(unsigned short idvalue)
>> {
>> switch (idvalue)
>> {
>> case Square:
>> return new Square();
>> case Circle:
>> return new Circle();
>> case Pentagon:
>> return new Pentagon();
>> case Sheep:
>> return new Sheep();
>> }
>> }
>>
>> ... which is the kind of code that gets important pretty quickly once you
>> start worry about serialization of data over a network, or to and from
>> files.[/color]
>
> Better, because this kind of code usually consolidates the switch in the one
> place. Note that this sort of construction doesn't come up that many times
> in the same program, because you only have one of these for each family of
> classes.
>[color=green]
>> Sure, you can craft your own table of values and function pointers, but
>> it's not as readable - and will never be as readable until C++/C/C# gets
>> better support for table-driving data structures.[/color]
>
> see std::map. The clean way to implement the above in c++ is to have a
> std::map<int, Object*> and use a clone() method to get your prototype.
> The translation unit that defines the subclass in question takes care of
> inserting the instance into the map.
>
> e.g.
>
> template <class T> insert_into_map {
> insert_into_map(std::string s) {
> T* x = new T;
> the_map().insert(s,x); // the_map is the global table
> }
> };
>
> class Circle {
>
> ....
>
> };
> namespace {
> insert_into_map <Circle> x("Circle);
> };
>
> Object* make_object(const std::string & id){
> std::map<std::string,Object*>::iterator it = the_map().find(id);
> if (it==the_map().end())
> return NULL;
> else return it->second->clone();
> }
>
> This also has the advantage that you can populate the map at runtime if you
> dynamically load the code that defines circle (so you get runtime plugin
> support)[/color]
It also has the disadvantage that you're using variable length strings as
your key for your map, you're using a tree for the lookup instead of a
hashtable or a trie (woe betide if you ever have to use this in a fast
fashion), and it adds to your initialization time. Not to mention the
potential memory cost of all of these Object's that you need to clone
later.
There's no point making code extensible unless you plan to extend it. Wrap
it in a function, and you can replace with an extensible version later.
Most cases, however, will survive perfectly well with the much faster
switch() statement.
--
People in the killfile (and whose posts I won't read) as of 4/9/2004
1:56:00 PM:
Peter Kohlmann, T.Max Devlin. Matt Templeton (scored down) | | | | re: the c# return statement
In comp.os.linux.advocacy, John Bailo
<jabailo@earthlink.net>
wrote
on Thu, 08 Apr 2004 22:33:22 GMT
<26a5ed68577bc637437dd356fb3129d2@news.teranews.co m>:[color=blue]
>
> The c# *return* statement has been bothering me the past few months.
>
> I don't like the fact that you can have different code paths in a method
> and have multiple return statements. To me, it would be more orthogonal
> if a method could only have one return statement.
>[/color]
Java has the exact same problem.
I'll admit to some curiosity but one way around this issue may be
at the code generation level of the compiler: briefly, if one codes
public int doSomething()
{
if(badThing())
return -1;
/* something */
if(anotherbadThing())
return -2;
int retv = /* whatever */;
/* more something */
return retv;
}
one might get:
* * *
_doSomething:
CALLS #0, badThing
TST.L R0
JZ $1
MOV.L #-1, R0
JMP _doSomething$Return
$1:
/* something */
CALLS #0, anotherbadThing
TST.L R0
JZ $2
MOV.L #-2, R0
JMP _doSomething$Return
$2:
/* whatever */
MOV.L (whatever),R1 ; we're assuming retv is cached in a reg here
/* more something */
MOV.L R1, R0
_doSomething$Return:
RET
* * *
where only one RET is in the routine -- and the compiler is responsible
for ensuring that each other return statement has R0 loaded properly
and jumping thereto.
(The assembly language is a corruption of VAX assembly, which I happen
to like. MOV a, b stores a into b. '#' indicates immediate.
R0 and R1 are registers. TST.L tests a value and sets condition
flags which JZ tests. JMP, erm, jumps. $n is a local label, a
useful concept in some assemblers. CALLS calls a routine, using
parameters on the stack; VAX also supports CALLG, which uses
a preformatted argument list -- useful for very old FORTRAN and
COBOL dialects that did not support recursion. RET of course returns.)
Of course I for one feel this is mostly a philosophical dispute.
If the issue is code clarity, one can work around it in various fashions.
--
#191, ewill3@earthlink.net
It's still legal to go .sigless. | | | | re: the c# return statement
In comp.os.linux.advocacy, John Bailo
<jabailo@earthlink.net>
wrote
on Thu, 08 Apr 2004 22:33:22 GMT
<26a5ed68577bc637437dd356fb3129d2@news.teranews.co m>:[color=blue]
>
> The c# *return* statement has been bothering me the past few months.
>
> I don't like the fact that you can have different code paths in a method
> and have multiple return statements. To me, it would be more orthogonal
> if a method could only have one return statement.
>[/color]
Java has the exact same problem.
I'll admit to some curiosity but one way around this issue may be
at the code generation level of the compiler: briefly, if one codes
public int doSomething()
{
if(badThing())
return -1;
/* something */
if(anotherbadThing())
return -2;
int retv = /* whatever */;
/* more something */
return retv;
}
one might get:
* * *
_doSomething:
CALLS #0, badThing
TST.L R0
JZ $1
MOV.L #-1, R0
JMP _doSomething$Return
$1:
/* something */
CALLS #0, anotherbadThing
TST.L R0
JZ $2
MOV.L #-2, R0
JMP _doSomething$Return
$2:
/* whatever */
MOV.L (whatever),R1 ; we're assuming retv is cached in a reg here
/* more something */
MOV.L R1, R0
_doSomething$Return:
RET
* * *
where only one RET is in the routine -- and the compiler is responsible
for ensuring that each other return statement has R0 loaded properly
and jumping thereto.
(The assembly language is a corruption of VAX assembly, which I happen
to like. MOV a, b stores a into b. '#' indicates immediate.
R0 and R1 are registers. TST.L tests a value and sets condition
flags which JZ tests. JMP, erm, jumps. $n is a local label, a
useful concept in some assemblers. CALLS calls a routine, using
parameters on the stack; VAX also supports CALLG, which uses
a preformatted argument list -- useful for very old FORTRAN and
COBOL dialects that did not support recursion. RET of course returns.)
Of course I for one feel this is mostly a philosophical dispute.
If the issue is code clarity, one can work around it in various fashions.
--
#191, ewill3@earthlink.net
It's still legal to go .sigless. | | | | re: the c# return statement
The Ghost In The Machine wrote:
[color=blue]
> In comp.os.linux.advocacy, John Bailo
> <jabailo@earthlink.net>
> wrote
> on Thu, 08 Apr 2004 22:33:22 GMT
> <26a5ed68577bc637437dd356fb3129d2@news.teranews.co m>:[color=green]
>>
>> The c# *return* statement has been bothering me the past few months.
>>
>> I don't like the fact that you can have different code paths in a method
>> and have multiple return statements. To me, it would be more orthogonal
>> if a method could only have one return statement.
>>[/color]
>
> Java has the exact same problem.
>
> I'll admit to some curiosity but one way around this issue may be
> at the code generation level of the compiler: briefly, if one codes
>
> public int doSomething()
> {
> if(badThing())
> return -1;
>
> /* something */
>
> if(anotherbadThing())
> return -2;
>
> int retv = /* whatever */;
>
> /* more something */
>
> return retv;
> }
>
> one might get:
>
> * * *
>
> _doSomething:
>
> CALLS #0, badThing
> TST.L R0
> JZ $1
> MOV.L #-1, R0
> JMP _doSomething$Return
>
> $1:
> /* something */
>
> CALLS #0, anotherbadThing
> TST.L R0
> JZ $2
> MOV.L #-2, R0
> JMP _doSomething$Return
>
> $2:
>
> /* whatever */
>
> MOV.L (whatever),R1 ; we're assuming retv is cached in a reg here
>
> /* more something */
>
> MOV.L R1, R0
>
> _doSomething$Return:
>
> RET
>
> * * *
>
> where only one RET is in the routine -- and the compiler is responsible
> for ensuring that each other return statement has R0 loaded properly
> and jumping thereto.
>
> (The assembly language is a corruption of VAX assembly, which I happen
> to like. MOV a, b stores a into b. '#' indicates immediate.
> R0 and R1 are registers. TST.L tests a value and sets condition
> flags which JZ tests. JMP, erm, jumps. $n is a local label, a
> useful concept in some assemblers. CALLS calls a routine, using
> parameters on the stack; VAX also supports CALLG, which uses
> a preformatted argument list -- useful for very old FORTRAN and
> COBOL dialects that did not support recursion. RET of course returns.)
>
> Of course I for one feel this is mostly a philosophical dispute.
> If the issue is code clarity, one can work around it in various fashions.
>[/color]
what bothers me is that /return/ is both a *path* and a *value*
it sets a value to be passed to a method
*and*
it is a control statement -- offering break;
to me -- not good...
--
W '04 <:> Open | | | | re: the c# return statement
The Ghost In The Machine wrote:
[color=blue]
> In comp.os.linux.advocacy, John Bailo
> <jabailo@earthlink.net>
> wrote
> on Thu, 08 Apr 2004 22:33:22 GMT
> <26a5ed68577bc637437dd356fb3129d2@news.teranews.co m>:[color=green]
>>
>> The c# *return* statement has been bothering me the past few months.
>>
>> I don't like the fact that you can have different code paths in a method
>> and have multiple return statements. To me, it would be more orthogonal
>> if a method could only have one return statement.
>>[/color]
>
> Java has the exact same problem.
>
> I'll admit to some curiosity but one way around this issue may be
> at the code generation level of the compiler: briefly, if one codes
>
> public int doSomething()
> {
> if(badThing())
> return -1;
>
> /* something */
>
> if(anotherbadThing())
> return -2;
>
> int retv = /* whatever */;
>
> /* more something */
>
> return retv;
> }
>
> one might get:
>
> * * *
>
> _doSomething:
>
> CALLS #0, badThing
> TST.L R0
> JZ $1
> MOV.L #-1, R0
> JMP _doSomething$Return
>
> $1:
> /* something */
>
> CALLS #0, anotherbadThing
> TST.L R0
> JZ $2
> MOV.L #-2, R0
> JMP _doSomething$Return
>
> $2:
>
> /* whatever */
>
> MOV.L (whatever),R1 ; we're assuming retv is cached in a reg here
>
> /* more something */
>
> MOV.L R1, R0
>
> _doSomething$Return:
>
> RET
>
> * * *
>
> where only one RET is in the routine -- and the compiler is responsible
> for ensuring that each other return statement has R0 loaded properly
> and jumping thereto.
>
> (The assembly language is a corruption of VAX assembly, which I happen
> to like. MOV a, b stores a into b. '#' indicates immediate.
> R0 and R1 are registers. TST.L tests a value and sets condition
> flags which JZ tests. JMP, erm, jumps. $n is a local label, a
> useful concept in some assemblers. CALLS calls a routine, using
> parameters on the stack; VAX also supports CALLG, which uses
> a preformatted argument list -- useful for very old FORTRAN and
> COBOL dialects that did not support recursion. RET of course returns.)
>
> Of course I for one feel this is mostly a philosophical dispute.
> If the issue is code clarity, one can work around it in various fashions.
>[/color]
what bothers me is that /return/ is both a *path* and a *value*
it sets a value to be passed to a method
*and*
it is a control statement -- offering break;
to me -- not good...
--
W '04 <:> Open | | | | re: the c# return statement
In article <kei4vrq3oqt0$.1sh3cgo94pvmu$@fanatastical.malapro p.net>, Milo T. wrote:
[color=blue]
> It also has the disadvantage that you're using variable length strings as
> your key for your map, you're using a tree for the lookup instead of a
> hashtable or a trie (woe betide if you ever have to use this in a fast
> fashion), and it adds to your initialization time. Not to mention the[/color]
My understanding is that dynamic allocation is pretty slow so it should
dwarf the other stuff, but I don't really know because I haven't benchmarked
it -- never needed this idiom for fast allocation of small objects (I try to
avoid heavily dynamic code for very small objects that need to be processed
quickly). Could also depend partly on how good the allocator is.
Cheers,
--
Donovan Rebbechi http://pegasus.rutgers.edu/~elflord/ | | | | re: the c# return statement
In article <kei4vrq3oqt0$.1sh3cgo94pvmu$@fanatastical.malapro p.net>, Milo T. wrote:
[color=blue]
> It also has the disadvantage that you're using variable length strings as
> your key for your map, you're using a tree for the lookup instead of a
> hashtable or a trie (woe betide if you ever have to use this in a fast
> fashion), and it adds to your initialization time. Not to mention the[/color]
My understanding is that dynamic allocation is pretty slow so it should
dwarf the other stuff, but I don't really know because I haven't benchmarked
it -- never needed this idiom for fast allocation of small objects (I try to
avoid heavily dynamic code for very small objects that need to be processed
quickly). Could also depend partly on how good the allocator is.
Cheers,
--
Donovan Rebbechi http://pegasus.rutgers.edu/~elflord/ | | | | re: the c# return statement
> > I don't like the fact that you can have different code paths in a method[color=blue][color=green]
> > and have multiple return statements. To me, it would be more[/color][/color]
orthogonal[color=blue][color=green]
> > if a method could only have one return statement.
> >[/color]
>
> Java has the exact same problem.
>
> I'll admit to some curiosity but one way around this issue may be
> at the code generation level of the compiler: briefly, if one codes
>[/color]
[..][color=blue]
> where only one RET is in the routine -- and the compiler is responsible
> for ensuring that each other return statement has R0 loaded properly
> and jumping thereto.
>
> (The assembly language is a corruption of VAX assembly, which I happen
> to like. MOV a, b stores a into b. '#' indicates immediate.
> R0 and R1 are registers.[/color]
Since when does the JVM use registers? It is stackbased.
--
cody
[Freeware, Games and Humor] www.deutronium.de.vu || www.deutronium.tk |  | | Similar .NET Framework bytes | | | /bytes/about
We are a network of experts and professionals in IT and software development that help one another with answers to tough questions and share insights.
Get the best answers to your questions from over 226,223 network members.
|