C# Methods with Multiple Boolean Arguments
Several Different Ways to Deal with It
Every once in awhile, the need seems to arise to create a method that takes a large number of boolean arguments. The method prototype would like something like the one shown below.
void MyMethod(
bool a,
bool b,
bool c,
bool d
);
From a usage viewpoint, it's generally a bad idea to put too many parameters of the same type in a row within a method's argument list; it becomes way to easy to accidentally get the values out of order while writing the code initially, when additional parameters are added later, when merging with changes made by other developers, etc.
obj.MyCall(
true,
false,
false,
true
);
This post presents several different solutions for dealing with a large number of boolean arguments for the same method. Some of these are pretty obvious, while others are patterns you may not have thought of before:
- Do nothing special; just use a bunch of boolean arguments
- Add per-argument comments where called
- Create a struct or class that contains all of the arguments, and pass that in
- Create a flags enum, and pass that in
- Create a dedicated enum per argument, and pass those in
- Use named arguments
Do Nothing Special
It should be pretty obvious to see why this is "bad".
void MyMethod(
bool a,
bool b,
bool c,
bool d
);
For example, let's say another developer reordered the arguments in the list to something that seemed "more natural" to that developer.
void MyMethod(
bool a,
bool d,
bool b,
bool c
);
All existing code that calls that method would have to change.
// Old Usage
obj.MyCall(
true,
false,
false,
true
);
// New Usage
obj.MyCall(
true,
true,
false,
false
);
And, in that case, if the existing code is not changed it will still compile but the arguments would be in the wrong order. Furthermore, if you started doing a search for all usages of MyMethod() and started making the necessary changes, you could easily loose track of which usages you have already fixed and which ones you have not fixed.
As another example, let's say another developer added a new argument into the middle of the list.
void MyMethod(
bool a,
bool e, //<--- New Argument
bool b,
bool c,
bool d
);
All existing usages would have to change.
// Old Usage
obj.MyCall(
true,
false,
false,
true);
// New Usage
obj.MyCall(
true,
true, //<--- New Argument
false,
false,
true);
At least that code would not compile, requiring you to fix it in all places. But it would still be challenging to know where the change should go, especially, if the changes you made need to be merged with changes made by another developer.
Add Per-Argument Comments Where Called
To help reduce confusion, some people like to put per-argument comments on each usage. Although this does no eliminate all of the issues, it does help to reduce confusion.
obj.MyMethod(
// a
true,
// b
false,
// c
false,
// d
true);
obj.MyMethod(
true, // a
false, // b
false, // c
true // d
);
I personally like end-of-line comments on short lines, like the way I have it formatted in that second call above. But, I know some developers are dead set against end-of-line comments because of the confusion it causes if they accidentally get wrapped.
obj.SomeMethod(
true, // A
false);
obj.SomeMethod(
true,
// A
false);
Create Struct or Class
Sometimes, it is advantageous to create a separate class that has the arguments. It is really pretty easy, especially if you do not care about the mutability of the property values, and are willing to use auto properties and object initializers.
For example, instead of separate arguments, you would do something like this.
class MyMethodArguments
{
public bool A { get; set; }
public bool B { get; set; }
public bool C { get; set; }
public bool D { get; set; }
}
void MyMethod(MyMethodArguments args);
obj.MyMethod(
new MyMethodArguments()
{
A = true,
B = false,
C = false,
D = true
} );
Create Flags Enum
It is also possible to use a flags enum, with bit code values.
[Flags]
public enum MyMethodArgumentsEnum
{
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
D = 1 << 3
}
void MyMethod(MyMethodArgumentsEnum flags);
obj.MyMethod(
MyMethodArgumentsEnum.A || MyMethodArgumentsEnum.D
);
This seems very easy an natural too me, bit I really enjoy low-level bit manipulation anyway. Many developers think flags enums are too low-level, and should be avoided in high-level languages such as C#.
Create Dedicated Enum per Argument
This optional requires more typing, but makes the usages quite easy to read because each argument can use Yes/No, True/False, On/Off, One/Zero or whatever terminology is appropriate for the specific argument.
public enum A
{
No
Yes,
}
public enum B
{
False,
True
}
public enum C
{
Off,
On
}
public enum D
{
Zero,
One
}
void MyMethod(
A a,
B b,
C c,
D d
);
obj.MyMethod(
A.Yes,
B.False,
C.Off,
D.One
);
I would not really consider this for a method used in only a few places, because it would not be worth the overhead of creating the enums. But, it could be very useful for commonly used methods, or in cases where the enums could be used across multiple methods with some of the same argument names.
Use Named Arguments
Starting with C# Language Specification 4.0 (VisualStudio 2010), you can use named arguments. The named arguments language syntax allows you to specify the arguments in any order as long as you specify the names along with the values.
Named arguments are generally used with optional arguments. However, there is no requirement to do so. You can use names arguments with both required and optional arguments. Therefore, it gives you the flexibility to decide whether you want the calling code to have to specify all of the arguments or not.
// This method has required arguments
void MyMethod_1(
bool a,
bool b,
bool c,
bool d
);
// This method has optional arguments with default values
void MyMethod_2(
bool a = false,
bool b = false,
bool c = false,
bool d = false
);
// This call...
obj.MyMethod_1(
a: true,
b: false,
c: false,
d: true
);
// ...compiles to the exact same code as this call
obj.MyMethod_1(
a: true,
d: true,
b: false,
c: false
);
// This call does not require specifying all of the values
ob.MyMethod_2(
a: true,
d: true
);
Programmer, Engineer