Daily Knowledge Drop
When defining string with constants (in particular, but other situations also apply) the nameof operator
can be leveraged to remove explicitly set values, ensuring more consistent, cleaner and less error prone code is produced.
nameof expression
So briefly, what does nameof
do? - it produces the name of a variable type, or member as the string constant.
Consider the following code:
var fibonacci = new List<int> { 0, 1, 1, 2, 3, 5 } ;
Console.WriteLine(nameof(List<int>)); // outputs the type
Console.WriteLine(nameof(fibonacci)); // outputs the variable name
Console.WriteLine(nameof(fibonacci.Count)); // outputs the method name
The output is:
List
fibonacci
Count
So let's see how we can leverage nameof
in the following use cases.
Constants
Issue
Suppose our application has a list of system statuses stored as string constants
:
public class SystemStatus
{
public const string Starting = "Starting";
public const string Running = "Running";
public const string ShuttingDown = "ShuttingDown";
public const string Offline = "Offline";
}
With the usage and output being:
Console.WriteLine(SystemStatus.ShuttingDown); // outputs "ShuttingDown"
Is there anything inherently wrong
with this approach? Not really, it still functions as expected.
However, suppose we need to change the "ShuttingDown" status to "Terminating"
. Navigating to one of the usages of SystemStatus.ShuttingDown, we rename the const
(using F2 Rename, for example).
We now have this:
Console.WriteLine(SystemStatus.Terminating); // still outputs "ShuttingDown"!
However the output is still "ShuttingDown"
, even though the const
name has been changed to Terminating! We now either have misleading code at best or a bug at worst.
Resolution
As this post implies, the solution is to use the nameof
operator instead of a hard-coded string!
Replacing the string values, with nameof
referencing the same variable:
public class SystemStatus
{
public const string Starting = nameof(Starting);
public const string Running = nameof(Running);
public const string ShuttingDown = nameof(ShuttingDown);
public const string Offline = nameof(Offline);
}
This for example will set the value of the Starting constants to the name of the constant, "Starting".
Now when renaming one of the const
names, it's value will be kept consistent - the same as the const
name. If manually renaming one of the const
names, but forgetting to rename its usage in nameof
will result in a compiler error:
public class SystemStatus
{
public const string Starting = nameof(Starting);
public const string Running = nameof(Running);
// Compiler error "ShuttingDown" does not exist
public const string Terminating = nameof(ShuttingDown);
public const string Offline = nameof(Offline);
}
The result - cleaner, more consistent and less error prone code!
Parameters
Issue
A similar issue can be experience in instances where a parameter name is used
, in an exception message, for example.
If we have this method, with the parameter name hardcoded in the exception string message:
public int Division(int dividend, int div)
{
if(div == 0)
{
// string value is hardcoded with the parameter name
throw new ArgumentException($"Parameter 'div' cannot be zero");
}
return dividend / div;
}
We realize the div
parameter is not a good parameter name, and should be renamed to divisor
:
public int Division(int dividend, int divisor)
{
if(divisor == 0)
{
// string value is hardcoded with the INCORRECT parameter name
throw new ArgumentException($"Parameter 'div' cannot be zero");
}
return dividend / divisor;
}
Its very easy to miss the hardcoded string when refactoring, especially if using an IDE rename feature, which won't automatically rename the hand-coded parameter name within the string.
Resolution
Again, the remedy is to use the nameof
operator:
public int Division(int dividend, int divisor)
{
if (divisor == 0)
{
throw new ArgumentException($"Parameter '{nameof(divisor)}' cannot be zero");
}
return dividend / divisor;
}
Now if renaming the parameter name, the exception message is automatically updated as well (or will throw a compiler error if not all usages of the parameter are not updated):
The result - cleaner, more consistent and less error prone code!
Notes
A small, simple technique to incorporate into daily coding, which may not have any obvious immediate benefit - but if/when it comes time to do any refactoring, will definitely save time and effort due to its cleaner, more consistent approach.
References
Daily Drop 119: 19-07-2022
At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.
The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.