Is there any way, perhaps by use of Contracts, to check whether a variable's value conforms to some rules during design time in Visual Studio 2022?
I.e. int is being used a lot in C# but quite often what we really want is uint, which is to say only positive values and the standard is to check if a negative value is passed to throw an error/exception/etc.
But would there be some way to write a method and tell Visual Studio or the compiler or whatever "this argument must be >= 0" and whenever a developer passes a value that is less than 0 an error be displayed in the Visual Studio list of errors?
Normally Code Contracts would be perfect for this - but seeming as the feature depressingly died unloved and abandoned in Microsoft's Great Leap Forward from .NET Framework to .NET Core, it means some alternative approach is required.
While it's entirely possible to basically reimplement Code Contracts using Roslyn Code Analysis, it is no small undertaking and will likely take you many months to build an analyzer that can provably verify the possible runtime bounds of a variable through its lifetime (which is, mathematically speaking, impossible to solve in the general case).
So the alternative is to use a refinement type / predicate-type / dependent-type (I forget the exact differences between them) - but the overall gist is that you use a new, distinct type to represent the constraints on its contained value. As C# is statically-typed, it means that types can be used to represent runtime state invariants.
In C#, these types are usually implemented as immutable
readonly structtypes because there's (usually) zero overhead. You can also combine this with operator overloading,IEquatable<T>, extension-methods, scopedoutparams, andimplicitconversions to make for a very ergonomic refinement-type experience.(This is where I really pity Java users, as Java doesn't have value-types, nor operator overloading, nor extension methods, nor user-defiend implicit conversions - ouch).
Note: when defining
implicitconversions it's very important that you only define implicit conversion from the (narrow) refined type back up to the wider type (as that's always going to succeed) - and you must never define implicit conversion from a wider type to the constrained type because if the wider value is invalid then that will cause a runtime exception when your validation code complains, which the compiler won't be able to pick-up on.So in your case, you want a type to represent a positive, non-zero
Int32value - so you'd want something like this:(This code omits implementing the
struct/IEquatable<>boilerplate that VS loves to complain about - but it's included in the*.snippetversion below).Here's my own personal refinement-type
*.snippetfor Visual Studio - I hope it works for you: