Skip to content

Instantly share code, notes, and snippets.

@daiplusplus
Created March 7, 2022 13:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daiplusplus/ef9fd596400de163be3671c768ab3757 to your computer and use it in GitHub Desktop.
Save daiplusplus/ef9fd596400de163be3671c768ab3757 to your computer and use it in GitHub Desktop.
Refinement type struct snippet
<?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. &quot;ValidatedEmailAddress&quot; (&quot;ValidatedEmailAddressUser&quot;)</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. &quot;User&quot; (for &quot;ValidatedEmailAddressUser&quot;)</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