Created
July 23, 2016 15:10
-
-
Save binki/72cc0d94a3b7f2d51be0be35a4317279 to your computer and use it in GitHub Desktop.
Using struct as a parameter list guard
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace ParameterDefaultsGuard | |
{ | |
// The goal is to make it hard to accidentally call the wrong | |
// overload when wanting to provide multiple overloads and still | |
// allow overriding defaults through named parameters. To | |
// accomplish this, we create an empty struct type. This guards | |
// against passing in null to various positional parameters and | |
// that being mistakenly passed as the guard. The only way to | |
// instantiate such a guard is to | |
// | |
// 1. define a class member with its type as MyGuard | |
// | |
// 2. use default(MyGuard) (which we can use in our parameter | |
// lists). | |
// | |
// So callers will not accidentally get past the guard. This | |
// enables you to avoid the C# compiler complaining about multiple | |
// overloads matching. | |
class Program | |
{ | |
struct MyGuard { private MyGuard(int x) { } } | |
static void Do(MyGuard _guard = default(MyGuard), object y = null) | |
=> Console.WriteLine(new { _guard, y, }); | |
static void Do(object x, MyGuard _guard = default(MyGuard), object y = null) | |
=> Console.WriteLine(new { x, _guard, y, }); | |
static void InvertedDo(object y = null) | |
=> Console.WriteLine(new { y, }); | |
static void InvertedDo(object x, object y = null) | |
=> Console.WriteLine(new { x, y, }); | |
static void Main(string[] args) | |
{ | |
Do(); // { _guard = ParameterDefaultsGuard.Program+MyGuard, y = } | |
// Without guard, this would be ambiguous between the two Do() overloads. | |
Do(null); // { x = , _guard = ParameterDefaultsGuard.Program+MyGuard, y = } | |
// “error CS1503: Argument 1: cannot convert from '<null>' to 'Program.MyGuard'” | |
//Do(null, null); | |
// Providing y | |
Console.WriteLine(); | |
Do(y: 3); // { _guard = ParameterDefaultsGuard.Program+MyGuard, y = 3 } | |
Do(null, y: 3); // { x = , _guard = ParameterDefaultsGuard.Program+MyGuard, y = 3 } | |
// Unwanted behavior with ambiguous overloads. | |
Console.WriteLine(); | |
// Fully unambiguous. | |
InvertedDo(); // { y = } | |
// Compiler uses the one with 1 parameter even though I wanted to specify x. | |
InvertedDo(null); // { y = } | |
// Calls the one I want. | |
InvertedDo(y: null); // { y = } | |
// Calls the one I want. | |
InvertedDo(x: null); // { x = , y = } | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment