dotnet
dotnet copied to clipboard
Make Guard extensible
Overview
Although the Guard
class is a good tool to validate parameters in an uniform way, it is limited to the built-in checks and cannot be extended with application or library specific checks.
Being able to do so would improve code quality. Parameter validation could be performt using a single syntax/pattern. Library authors could deliver checks that help their users to use the library better,
API breakdown
/*
* Extend Guard with a static property named 'That' of type Guard`.
* This will be the new anchor to provide and perform checks.
*
* The same approach used in the 'Assert' class of 'Microsoft.VisualStudio.TestTools.UnitTesting' to make it extensible.
*/
public sealed partial class Guard
{
// Prevent instances other then 'That'.
private Guard() {}
// Extensibility point
public static Guard That { get; } = new();
}
/*
* Rebuild the existing checks as extension methods on 'Guard'.
*
* The existing checks could be marked as 'Obsolete'.
*
* New checks can easily be added as extension methods, inside and outside the package.
*/
public static class GuardExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNull<T>(this Guard guard, T? value, [CallerArgumentExpression("value")] string name = "")
{
if (value is null)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNull(value, name);
}
}
Usage example
class MyClass
{
public void MyMethod(object param1, string param2)
{
Guard.That.IsNotNull(param1);
Guard.That.MyCheck(param2);
}
}
Breaking change?
No
Alternatives
Alternative 1
Making the existing checks instance methods would allow the same syntax, but would break binary compatibility.
Alternative 2
Distibuting Guard
as source code package would make it extensible as Guard
is a partial class. But it would be difficult to expose new checks in libraries. If more than one package reexposes Guard
you will get ambiguity trouble.
Additional context
No response
Help us help you
Yes, I'd like to be assigned to work on this item
I would also like this feature. Ardalis already has a popular project, but having it in this package makes sense.
I have a preference for using his same extensibility point as I find it clearer and more readable, Guard.Against
.
The current way seems backwards to me. But if the team wants to stick with the current direction, then I would recommend Guard.Ensures
.
@Sergio0694 Any thoughts on this?
+1 for Against
, with Ensure
(no final s
) as a fallback preference.
This is not something we're planning on adding. Furthermore, it would also likely make the codegen worse. Extensibility is something that should eventually be solved once C# gets support for shapes/extensions.
The shapes/extensions is a proposal, it's not planned work. But why introduce a half-baked feature instead of just adding a dependency to an existing library like Ardalis.Guard?
It's not a half-baked feature. The fact it's not extensible doesn't make it so, that just wasn't one of the design goals. Furthermore, as I've mentioned, switching to instance methods would be a massive breaking change and also likely cause codegen regressions, which we do not want. These APIs are specifically designed to make the codegen as compact as possible.
"instead of just adding a dependency to an existing library like Ardalis.Guard?"
Taking a dependency on a 3rd party library would be a complete non-starter for any package in the Toolkit.
But it should have been a design goal. Why would developers want to use a feature that only works in the scenarios that the author chooses?
Also, we wouldn't need to switch to instance methods, we would be adding an extensibility point and methods to call the existing static methods, but now developers can extend it.
As for codegen, I don't understand the issue. Are you referring to source code generators or IL?