Created
March 7, 2022 13:45
-
-
Save daiplusplus/ef9fd596400de163be3671c768ab3757 to your computer and use it in GitHub Desktop.
Refinement type struct snippet
This file contains 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
<?xml version="1.0" encoding="utf-8"?> | |
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> | |
<!-- | |
Usage/installation instructions: | |
1. Save to a file `refine.snippet` somewhere (e.g. in `C:\Users\You\Documents\Visual Studio {year}\Code Snippets\Visual C#\My Code Snippets`). | |
* If saved outside your `Visual Studio {year}` folder, or if it isn't detected, add it manually via <kbd>Tools > Code Snippets Manager...</kbd> (Tip: ensure the top "Language" drop-down says "CSharp" as it defaults to ASP.NET for some reason). | |
2. To try it out, open a .cs file and move your cursor/caret to inside a `namespace`, then type the word "`refine`" and IntelliSense should list it as a snippet in the completion-list popup. | |
* If it doesn't appear despite being recognized by Code Snippets Manager ensure VS is configured to list Snippets in the code completion list (Tools > Options > Text Editor > C# > IntelliSense > Snippets behavior > "Always include snippets"). | |
3. Press <kbd>Tab</kbd> once or twice (it varies...) and it should be inserted, with the caret moved to the first `$refinementname$` placeholder. Type the new value then press <kbd>Tab</kbd> to move to the $supertypename$ placeholder. Press <kbd>Tab</kbd> or <kbd>Enter</kbd> when done. | |
--> | |
<CodeSnippet Format="1.0.0"> | |
<Header> | |
<Title>refine</Title> | |
<Shortcut>refine</Shortcut> | |
<SnippetTypes> | |
<!-- There are only 2 types of Snippets: "Expansion" and "Surround-With", btw: https://docs.microsoft.com/en-us/visualstudio/ide/code-snippets?view=vs-2022 --> | |
<SnippetType>Expansion</SnippetType> | |
</SnippetTypes> | |
<Description>Refinment type represented by a public readonly struct with implicit conversion support.</Description> | |
</Header> | |
<Snippet> | |
<Declarations> | |
<Object Editable="true"> | |
<ID>refinementname</ID> | |
<Type>Object</Type> | |
<ToolTip>PascalCased summary of the refinement type's predicate - this is concatenated with $supertype$ for the final struct name. e.g. "ValidatedEmailAddress" ("ValidatedEmailAddressUser")</ToolTip> | |
<Default>NewRefinement</Default> | |
</Object> | |
<Object Editable="true"> | |
<ID>supertype</ID> | |
<Type>Object</Type> | |
<ToolTip>The name of the type that is being refined. e.g. "User" (for "ValidatedEmailAddressUser")</ToolTip> | |
<Default>SupertypeName</Default> | |
</Object> | |
</Declarations> | |
<!-- Inside <Code>, the only reserved-token names are `$end$` and `$selected$`. Both can only be used at-most once. --> | |
<!-- BTW, for this snippet specifically, should the `.Value` property's getter self-validating? or always trust the constructor instead? What's the best way to prevent `default(StructType)` thesedays? --> | |
<Code Language="CSharp" Kind="type decl"><![CDATA[ | |
public static partial class RefinementExtensions | |
{ | |
public static Boolean Is$refinementname$$supertype$( this $supertype$ value, [NotNullWhen(true)] out $refinementname$$supertype$? valid ) | |
{ | |
return $refinementname$$supertype$.TryCreate( value, out valid ); | |
} | |
/// <summary>Throws <see cref="ArgumentException"/> if <paramref name="value"/> does not satisfy the refinement predicate.</summary> | |
/// <exception cref="ArgumentException"></exception> | |
public static $refinementname$$supertype$ To$refinementname$$supertype$( this $supertype$ value ) | |
{ | |
return $refinementname$$supertype$.Create( value ); | |
} | |
} | |
public readonly struct $refinementname$$supertype$ : IReadOnly$supertype$, IEquatable<$refinementname$$supertype$>, IEquatable<$supertype$> | |
{ | |
#region Create / TryCreate | |
/// <summary>Throws <see cref="ArgumentException"/> if <paramref name="value"/> does not satisfy the refinement predicate.</summary> | |
/// <exception cref="ArgumentException"></exception> | |
public static $refinementname$$supertype$ Create( $supertype$ value ) | |
{ | |
if( TryCreate( value, out $refinementname$$supertype$? valid ) ) return valid.Value; | |
else throw new ArgumentException( paramName: nameof(value), message: "Argument object does not satisfy " + nameof($refinementname$$supertype$) + "'s refinement predicate." ); | |
} | |
/// <summary>Returns <see langword="null"/> if <paramref name="value"/> does not satisfy the refinement predicate.</summary> | |
public static $refinementname$$supertype$? TryCreate( $supertype$ value ) | |
{ | |
return TryCreate( value, out $refinementname$$supertype$? valid ) ? valid : null; | |
} | |
/// <summary>Returns <see langword="false"/> if <paramref name="value"/> does not satisfy the refinement predicate.</summary> | |
public static Boolean TryCreate( $supertype$ value, [NotNullWhen(true)] out $refinementname$$supertype$? valid ) | |
{ | |
if( CONDITION ) | |
{ | |
valid = new $refinementname$$supertype$( value ); | |
return true; | |
} | |
return false; | |
} | |
#endregion | |
public static implicit operator $supertype$( $refinementname$$supertype$ self ) | |
{ | |
return self.Value; | |
} | |
private $refinementname$$supertype$( $supertype$ value ) | |
{ | |
this.value_doNotReadThisFieldExceptViaProperty = value ?? throw new ArgumentNullException(nameof(value)); | |
} | |
private readonly $supertype$ value_doNotReadThisFieldExceptViaProperty; | |
public $supertype$ Value => this.value_doNotReadThisFieldExceptViaProperty ?? throw new InvalidOperationException( "This " + nameof($refinementname$$supertype$) + " value is invalid." ); | |
public override String ToString() => this.Value.ToString(); | |
#region IReadOnly$supertype$ | |
// TODO? | |
#endregion | |
#region IEquatable<$refinementname$$supertype$>, IEquatable<$supertype$> | |
private Boolean IsDefault => this.value_doNotReadThisFieldExceptViaProperty is null; | |
public override Boolean Equals( Object? obj ) | |
{ | |
if( this.IsDefault ) | |
{ | |
return obj is null; | |
} | |
else if( obj is $supertype$ super ) | |
{ | |
return this.Equals( super: super ); | |
} | |
else if( obj is $refinementname$$supertype$ other ) | |
{ | |
return this.Equals( other: other ); | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
public Boolean Equals( $refinementname$$supertype$ other ) | |
{ | |
return ( this.IsDefault && other.IsDefault ) || ( this.Value == other.Value ); | |
} | |
public Boolean Equals( $supertype$? super ) | |
{ | |
return !this.IsDefault && ( this.Value == super ); | |
} | |
public override Int32 GetHashCode() | |
{ | |
if( this.IsDefault ) return 0; | |
return this.Value.GetHashCode(); // return HashCode.Combine( this.Value ); | |
} | |
public static Boolean operator ==( $refinementname$$supertype$ left, $refinementname$$supertype$ right ) | |
{ | |
return left.Equals( other: right ); | |
} | |
public static Boolean operator !=( $refinementname$$supertype$ left, $refinementname$$supertype$ right ) | |
{ | |
return !left.Equals( other: right ); | |
} | |
#endregion | |
}$end$]]> | |
</Code> | |
<Imports> | |
<Import> | |
<Namespace>System</Namespace> | |
</Import> | |
<Import> | |
<Namespace>System.Diagnostics.CodeAnalysis</Namespace> | |
</Import> | |
</Imports> | |
</Snippet> | |
</CodeSnippet> | |
</CodeSnippets> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment