19

I have run across a bunch of code in a few C# projects that have the following constants:

    const int ZERO_RECORDS = 0;
    const int FIRST_ROW = 0;
    const int DEFAULT_INDEX = 0;
    const int STRINGS_ARE_EQUAL = 0;

Has anyone ever seen anything like this? Is there any way to rationalize using constants to represent language constructs? IE: C#'s first index in an array is at position 0. I would think that if a developer needs to depend on a constant to tell them that the language is 0 based, there is a bigger issue at hand.

The most common usage of these constants is in handling Data Tables or within 'for' loops.

Am I out of place thinking these are a code smell? I feel that these aren't a whole lot better than:

const int ZERO = 0;
const string A = "A";
Bryan Rowe
  • 9,185
  • 4
  • 26
  • 34
  • they *are* better! the second one in your example won't compile ;) other than that, no, the top four examples seem rather redundant as well – David Hedlund Dec 07 '09 at 20:18
  • 2
    As a side note, it's always worth keeping in mind that _value_ of a `const` field is part of the public interface of your assembly, and changing it is likely to be a binary-level breaking change (as all existing assemblies compiled against old version will use the old value of the constant). None of the constants listed here seem to be endangered by this, but something like `public const int RECORDS_PER_PAGE=40;` is very likely to be, and quite often the line is blurry. – Pavel Minaev Dec 07 '09 at 20:29
  • What is with the votes to close? – Bryan Rowe Dec 07 '09 at 20:49
  • 3
    This is a little beside the point, but constants in C# should not be all-caps. See http://www.irritatedvowel.com/Programming/Standards.aspx, for example. – devuxer Dec 07 '09 at 22:36
  • 3
    I get constant abuse at my workplace, too! – Coxy Dec 08 '09 at 01:25

17 Answers17

12

Am I out of place thinking these are a code smell? I feel that these aren't a whole lot better than:

Compare the following:

if(str1.CompareTo(str2) == STRINGS_ARE_EQUAL) ...

with

if(str1.CompareTo(str2) == ZERO) ...
if(str1.CompareTo(str2) == 0) ...

Which one makes more immediate sense?

Anon.
  • 58,739
  • 8
  • 81
  • 86
  • 57
    str1.Equals(str2) makes sense :p – Tim Robinson Dec 07 '09 at 20:20
  • That is true. However, consider the case where we compare once and then switch based on the result. – Anon. Dec 07 '09 at 20:21
  • Id say the 2nd set of code makes more sense, if I see a named variable like STRINGS_ARE_EQUAL, it will strike me as odd and I will go look and see what the deal is. If i see "...CompareTo(str2) == 0)" or "str1.Equals(str2)" I instantly know what is happening and I can move on. – Tom Neyland Dec 07 '09 at 20:22
  • @Tnay: If you fell the need to look up the value of every named constant, I think that says more about the "smell" of your fellow developers than anything else. – Anon. Dec 07 '09 at 20:23
  • 1
    If you need to look up the value of named constants then something is horribly wrong with the design of the code. – Paul Sasik Dec 07 '09 at 20:24
  • 10
    Well, the very last one makes the most sense to me. Comparisons in .NET consistently return -N, 0, or N to indicate order, and if you're a .NET programmer and don't know that idiom, you should learn it. – mqp Dec 07 '09 at 20:25
  • 7
    I use == instead of .Equals for strings. For maximum readability. – recursive Dec 07 '09 at 20:26
  • 5
    What more, it's not just "in .NET" - it is a very old idiom, at least as old as C `strcmp`, and possibly older. It's also very widely used, including high-level languages. – Pavel Minaev Dec 07 '09 at 20:27
  • Anon- what's your opinion- you don't state it in your answer! – RichardOD Dec 07 '09 at 20:29
  • @recursive, don't forget someString.Equals(someObject) instead of someString == someObject – Tim Robinson Dec 07 '09 at 20:32
  • 4
    If you use 0, everybody with any experience knows what it is. Inexperienced developers might have to look it up, but they'll learn. Use STRINGS_ARE_EQUAL, and nobody will actually know what it is. They'll have a good idea, but the fact that something odd is happening will make them check. The more experienced ones will remember times they were badly bitten by assuming a constant was what they thought it was. – David Thornley Dec 07 '09 at 20:42
  • @recursive, @Tim Robinson: http://blogs.msdn.com/ericlippert/archive/2009/04/09/double-your-dispatch-double-your-fun.aspx has some interesting notes on this. – Jeremy McGee Dec 07 '09 at 21:40
  • I'd ask: Which makes more sense to someone who doesn't know anything about `string.CompareTo()`? – devuxer Dec 07 '09 at 23:20
  • 2
    If you have to do that much string comparison, define extension methods on string: `string.IsLessThan(string)` and/or `string.IsGreaterThan(string)`. But for equality, please, use `string.Equals(string)`. That's what it's for. – Ryan Lundy Dec 07 '09 at 23:39
10

Abuse, IMHO. "Zero" is just is one of the basics.

Although the STRINGS_ARE_EQUAL could be easy, why not ".Equals"?

Accepted limited use of magic numbers?

gbn
  • 422,506
  • 82
  • 585
  • 676
5

That definitely a code smell.

The intent may have been to 'add readability' to the code, however things like that actually decrease the readability of code in my opinion.

Tom Neyland
  • 6,860
  • 2
  • 36
  • 52
5

Some people consider any raw number within a program to be a 'magic number'. I have seen coding standards that basically said that you couldn't just write an integer into a program, it had to be a const int.

tloach
  • 8,009
  • 1
  • 33
  • 44
  • 1
    Yeah, I've run into this before. The code standard says "no magic numbers" and an overzealous SQA person says using 0 is a magic number. I'm imagining a frustrated programmer placating them with a quick series of consts. – John D. Dec 07 '09 at 20:24
  • 2
    i've seen this too. Thing is, there are always exceptions and zeros ought to be an exception to the magic numbers rule. – Paul Sasik Dec 07 '09 at 20:28
  • We did this at one place I worked, because we had offshore developers that were less than stellar. It was much easier to enforce "all raw numbers are magic numbers" than any other standard we could come up with. – Dean J Dec 07 '09 at 20:56
3

Am I out of place thinking these are a code smell? I feel that these aren't a whole lot better than:

const int ZERO = 0;

const int A = 'A';

Probably a bit of smell, but definitely better than ZERO=0 and A='A'. In the first case they're defining logical constants, i.e. some abstract idea (string equality) with a concrete value implementation.

In your example, you're defining literal constants -- the variables represent the values themselves. If this is the case, I would think that an enumeration is preferred since they rarely are singular values.

Community
  • 1
  • 1
micahtan
  • 18,530
  • 1
  • 38
  • 33
3

That is definite bad coding.

I say constants should be used only where needed where things could possible change sometime later. For instance, I have a lot of "configuration" options like SESSION_TIMEOUT defined where it should stay the same, but maybe it could be tweaked later on down the road. I do not think ZERO can ever be tweaked down the road.

Also, for magic numbers zero should not be included.

I'm a bit strange I think on that belief though because I would say something like this is going to far

//input is FIELD_xxx where xxx is a number
input.SubString(LENGTH_OF_FIELD_NAME); //cut out the FIELD_ to give us the number
Earlz
  • 62,085
  • 98
  • 303
  • 499
2

You should have a look at some of the things at thedailywtf

One2Pt20462262185th

and

Enterprise SQL

Adriaan Stander
  • 162,879
  • 31
  • 289
  • 284
2

I think sometimes people blindly follow 'Coding standards' which say "Don't use hardcoded values, define them as constants so that it's easier to manage the code when it needs to be updated' - which is fair enough for stuff like:

const in MAX_NUMBER_OF_ELEMENTS_I_WILL_ALLOW = 100

But does not make sense for:

if(str1.CompareTo(str2) == STRINGS_ARE_EQUAL)

Because everytime I see this code I need to search for what STRINGS_ARE_EQUAL is defined as and then check with docs if that is correct.

Instead if I see:

if(str1.CompareTo(str2) == 0)

I skip step 1 (search what STRINGS_ARE... is defined as) and can check specs for what value 0 means.

You would correctly feel like replacing this with Equals() and use CompareTo() in cases where you are interested in more that just one case, e.g.:

switch (bla.CompareTo(bla1))
{
     case IS_EQUAL:
     case IS_SMALLER:
     case IS_BIGGER:
     default:
}

using if/else statements if appropriate (no idea what CompareTo() returns ...)

I would still check if you defined the values correctly according to specs.

This is of course different if the specs defines something like ComparisonClass::StringsAreEqual value or something like that (I've just made that one up) then you would not use 0 but the appropriate variable.

So it depends, when you specifically need to access first element in array arr[0] is better than arr[FIRST_ELEMENT] because I will still go and check what you have defined as FIRST_ELEMENT because I will not trust you and it might be something different than 0 - for example your 0 element is dud and the real first element is stored at 1 - who knows.

stefanB
  • 77,323
  • 27
  • 116
  • 141
1

I'd go for code smell. If these kinds of constants are necessary, put them in an enum:

enum StringEquality
{
    Equal,
    NotEqual
}

(However I suspect STRINGS_ARE_EQUAL is what gets returned by string.Compare, so hacking it to return an enum might be even more verbose.)

Edit: Also SHOUTING_CASE isn't a particularly .NET-style naming convention.

Tim Robinson
  • 53,480
  • 10
  • 121
  • 138
1

i don't know if i would call them smells, but they do seem redundant. Though DEFAULT_INDEX could actually be useful.

The point is to avoid magic numbers and zeros aren't really magical.

Paul Sasik
  • 79,492
  • 20
  • 149
  • 189
  • 1
    yea default_index could be useful if you want to maintain forward compatability in the event that microsoft change from 0 based indexing to -13 based indexing.... – Paul Creasey Dec 07 '09 at 20:55
  • @Paul, I thought that was a feature in .NET 4.0 -- You can change your index base in the web/app config file. : ) – Bryan Rowe Dec 07 '09 at 21:00
1

Is this code something in your office or something you downloaded?

If it's in the office, I think it's a problem with management if people are randomly placing constants around. Globally, there shouldn't be any constants unless everyone has a clear idea or agreement of what those constants are used for.

In C# ideally you'd want to create a class that holds constants that are used globally by every other class. For example,

class MathConstants
{
 public const int ZERO=0;
}

Then in later classes something like:

....
if(something==MathConstants.ZERO)
...

At least that's how I see it. This way everyone can understand what those constants are without even reading anything else. It would reduce confusion.

Daniel
  • 75
  • 1
  • 5
  • 1
    What benefit would you get from MathConstants.ZERO ? – Bryan Rowe Dec 07 '09 at 22:07
  • 1
    What I'm getting at is that it is an agreed upon constant rather than constants that are written all over the place. This way, any future developer can easily understand constants. Like FIRST and SECOND, are confusing to me, but if they were agreed upon constants by the team, it would be far more understandable. At least that's how I see it. – Daniel Dec 08 '09 at 16:00
1

There are generally four reasons I can think of for using a constant:

  1. As a substitute for a value that could reasonably change in the future (e.g., IdColumnNumber = 1).
  2. As a label for a value that may not be easy to understand or meaningful on its own (e.g. FirstAsciiLetter = 65),
  3. As a shorter and less error-prone way of typing a lengthy or hard to type value (e.g., LongSongTitle = "Supercalifragilisticexpialidocious")
  4. As a memory aid for a value that is hard to remember (e.g., PI = 3.14159265)

For your particular examples, here's how I'd judge each example:

const int ZERO_RECORDS = 0;
// almost definitely a code smell

const int FIRST_ROW = 0;
// first row could be 1 or 0, so this potentially fits reason #2,
// however, doesn't make much sense for standard .NET collections
// because they are always zero-based

const int DEFAULT_INDEX = 0;
// this fits reason #2, possibly #1

const int STRINGS_ARE_EQUAL = 0;
// this very nicely fits reason #2, possibly #4
// (at least for anyone not intimately familiar with string.CompareTo())

So, I would say that, no, these are not worse than Zero = 0 or A = "A".

devuxer
  • 41,681
  • 47
  • 180
  • 292
  • first_row and default_index are just used when iterating through a loop. IE: (for int i = default_index...). first_row is used in the same way when iterating over a data table. – Bryan Rowe Dec 08 '09 at 14:58
  • If that's how they are used, I'd definitely say code smell then. – devuxer Dec 08 '09 at 17:48
0

If the zero indicates something other than zero (in this case STRINGS_ARE_EQUAL) then that IS Magical. Creating a constant for it is both acceptable and makes the code more readable.

Creating a constant called ZERO is pointless and a waste of finger energy!

Brian
  • 2,253
  • 2
  • 23
  • 39
  • 1
    I'd argue that zero isn't magical in that sense either. String.Compare follows the usual standard concerning return values to indicate success/failure -- what is so magical about that? – Bryan Rowe Dec 07 '09 at 20:43
  • It's magic because it indicates something other than zero. Readable code is readable code, standards or not. – Brian Dec 08 '09 at 12:31
  • 1
    0 is a well known return value. I think 0 meaning true/false, success/fail are all obvious. – Bryan Rowe Dec 08 '09 at 14:52
0

Smells a bit, but I could see cases where this would make sense, especially if you have programmers switching from language to language all the time.

For instance, MATLAB is one-indexed, so I could imagine someone getting fed up with making off-by-one mistakes whenever they switch languages, and defining DEFAULT_INDEX in both C++ and MATLAB programs to abstract the difference. Not necessarily elegant, but if that's what it takes...

Kena
  • 6,891
  • 5
  • 35
  • 46
0

Right you are to question this smell young code warrior. However, these named constants derive from coding practices much older than the dawn of Visual Studio. They probably are redundant but you could do worse than to understand the origin of the convention. Think NASA computers, way back when...

grenade
  • 31,451
  • 23
  • 97
  • 126
0

You might see something like this in a cross-platform situation where you would use the file with the set of constants appropriate to the platform. But Probably not with these actual examples. This looks like a COBOL coder was trying to make his C# look more like english language (No offence intended to COBOL coders).

Ken Lange
  • 953
  • 1
  • 7
  • 18
0

It's all right to use constants to represent abstract values, but quite another to represent constructs in your own language.

const int FIRST_ROW = 0 doesn't make sense.

const int MINIMUM_WIDGET_COUNT = 0 makes more sense.

The presumption that you should follow a coding standard makes sense. (That is, coding standards are presumptively correct within an organization.) Slavishly following it when the presumption isn't met doesn't make sense.

So I agree with the earlier posters that some of the smelly constants probably resulted from following a coding standard ("no magic numbers") to the letter without exception. That's the problem here.