diff --git a/src/Vogen.SharedTypes/StringComparisonDefault.cs b/src/Vogen.SharedTypes/StringComparisonDefault.cs
new file mode 100644
index 00000000000..ea72024f1c8
--- /dev/null
+++ b/src/Vogen.SharedTypes/StringComparisonDefault.cs
@@ -0,0 +1,17 @@
+using System.Diagnostics;
+// ReSharper disable UnusedMember.Global
+
+namespace Vogen;
+
+public enum StringComparisonDefault
+{
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ Unspecified = -1,
+ Omit = 0,
+ Ordinal = 1,
+ OrdinalIgnoreCase = 2,
+ CurrentCulture = 3,
+ CurrentCultureIgnoreCase = 4,
+ InvariantCulture = 5,
+ InvariantCultureIgnoreCase = 6,
+}
diff --git a/src/Vogen.SharedTypes/ValueObjectAttribute.cs b/src/Vogen.SharedTypes/ValueObjectAttribute.cs
index 419f8d9b7a8..a84e365cbae 100644
--- a/src/Vogen.SharedTypes/ValueObjectAttribute.cs
+++ b/src/Vogen.SharedTypes/ValueObjectAttribute.cs
@@ -1,4 +1,4 @@
-// ReSharper disable UnusedParameter.Local
+// ReSharper disable UnusedParameter.Local
// ReSharper disable NullableWarningSuppressionIsUsed
// ReSharper disable UnusedType.Global
@@ -88,6 +88,78 @@ public ValueObjectAttribute(
numericsGeneration)
{
}
+
+ ///
+ /// Configures aspects of this individual value object.
+ ///
+ /// Specifies what conversion code is generated - defaults to which generates type converters and a converter to handle serialization using System.Text.Json
+ /// Specifies the type of exception thrown when validation fails—defaults to .
+ /// Simple customization switches—defaults to .
+ /// Specifies how strict deserialization is, e.g. should your Validate method be called, or should pre-defined instances that otherwise invalid be allowed - defaults to .
+ /// Specifies the level that debug attributes are written as some IDEs don't support all of them, e.g., Rider - defaults to which generates DebuggerDisplay and a debugger proxy type for IDEs that support them
+ /// Species which comparison code is generated—defaults to which hoists any IComparable implementations from the primitive.
+ /// Specifies which string comparison code is generated—defaults to which doesn't generate anything related to string comparison.
+ /// Controls how cast operators are generated for casting from the Value Object to the primitive.
+ /// Options are implicit or explicit or none. Explicit is preferred over implicit if you really need them, but isn't recommended.
+ /// See for more information.
+ /// Controls how cast operators are generated for casting from the primitive to the Value Object.
+ /// Options are implicit or explicit or none. Explicit is preferred over implicit if you really need them, but isn't recommended.
+ /// See for more information.
+ ///
+ /// Specifies what is generated for IParsable types for strings - defaults to .
+ /// Specifies what is generated for Parse and TryParse methods - defaults to .
+ /// Specifies what to write for TryFrom methods—defaults to .
+ /// Specifies whether to generate an IsInitialized() method - defaults to .
+ ///
+ /// Specifies whether to generate primitive comparison operators, allowing this type to be compared for equality to the primitive.
+ /// Defaults to
+ ///
+ ///
+ /// var vo = MyInt.From(123);
+ ///
+ ///
+ /// bool same = vo == 123;
+ ///
+ ///
+ ///
+ /// Specifies whether to generate numeric interfaces (INumber<T> or INumberBase<T> depending on the underlying type)—defaults to .
+ /// Specifies the default used for ==, Equals, and GetHashCode on string-backed value objects—defaults to which uses the underlying string's default comparison.
+ public ValueObjectAttribute(
+ Conversions conversions,
+ Type? throws,
+ Customizations customizations,
+ DeserializationStrictness deserializationStrictness,
+ DebuggerAttributeGeneration debuggerAttributes,
+ ComparisonGeneration comparison,
+ StringComparersGeneration stringComparers,
+ CastOperator toPrimitiveCasting,
+ CastOperator fromPrimitiveCasting,
+ ParsableForStrings parsableForStrings,
+ ParsableForPrimitives parsableForPrimitives,
+ TryFromGeneration tryFromGeneration,
+ IsInitializedMethodGeneration isInitializedMethodGeneration,
+ PrimitiveEqualityGeneration primitiveEqualityGeneration,
+ NumericsGeneration numericsGeneration,
+ StringComparisonDefault stringDefaultComparison) : base(
+ typeof(T),
+ conversions,
+ throws,
+ customizations,
+ deserializationStrictness,
+ debuggerAttributes,
+ comparison,
+ stringComparers,
+ toPrimitiveCasting,
+ fromPrimitiveCasting,
+ parsableForStrings,
+ parsableForPrimitives,
+ tryFromGeneration,
+ isInitializedMethodGeneration,
+ primitiveEqualityGeneration,
+ numericsGeneration,
+ stringDefaultComparison)
+ {
+ }
}
///
@@ -100,9 +172,9 @@ public class ValueObjectAttribute : Attribute
// keep this signature in-line with `VogenConfiguration`
// as the syntax/semantics are read in the generator
// using parameter indexes (i.e. it expected param 0 to be the underlying type etc).
-
+
// ReSharper disable once MemberCanBeProtected.Global
-
+
///
/// Configures aspects of this individual value object.
///
@@ -156,5 +228,61 @@ public ValueObjectAttribute(
// of parameters is a binary-breaking change. See https://github.com/dotnet/runtime/issues/103722
// for more information.
}
+
+ ///
+ /// Configures aspects of this individual value object.
+ ///
+ /// The type of the primitive that is being wrapped—defaults to int.
+ /// Specifies what conversion code is generated - defaults to which generates type converters and a converter to handle serialization using System.Text.Json
+ /// Specifies the type of exception thrown when validation fails—defaults to .
+ /// Simple customization switches—defaults to .
+ /// Specifies how strict deserialization is, e.g. should your Validate method be called, or should pre-defined instances that otherwise invalid be allowed - defaults to .
+ /// Specifies the level that debug attributes are written as some IDEs don't support all of them, e.g., Rider - defaults to which generates DebuggerDisplay and a debugger proxy type for IDEs that support them
+ /// Species which comparison code is generated—defaults to which hoists any IComparable implementations from the primitive.
+ /// Specifies which string comparison code is generated—defaults to which doesn't generate anything related to string comparison.
+ /// Specifies the type of casting from wrapper to primitive - defaults to .
+ /// Specifies the type of casting from primitive to wrapper - default to .
+ /// Specifies what is generated for IParsable types for strings - defaults to .
+ /// Specifies what is generated for Parse and TryParse methods - defaults to .
+ /// Specifies what to write for TryFrom methods—defaults to .
+ /// Specifies whether to generate an IsInitialized() method - defaults to .
+ ///
+ /// Specifies whether to generate primitive comparison operators, allowing this type to be compared for equality to the primitive.
+ /// Defaults to
+ ///
+ ///
+ /// var vo = MyInt.From(123);
+ ///
+ ///
+ /// bool same = vo == 123;
+ ///
+ ///
+ ///
+ /// Specifies whether to generate numeric interfaces (INumber<T> or INumberBase<T> depending on the underlying type)—defaults to .
+ /// Specifies the default used for ==, Equals, and GetHashCode on string-backed value objects—defaults to which uses the underlying string's default comparison.
+ public ValueObjectAttribute(
+ Type? underlyingType,
+ Conversions conversions,
+ Type? throws,
+ Customizations customizations,
+ DeserializationStrictness deserializationStrictness,
+ DebuggerAttributeGeneration debuggerAttributes,
+ ComparisonGeneration comparison,
+ StringComparersGeneration stringComparers,
+ CastOperator toPrimitiveCasting,
+ CastOperator fromPrimitiveCasting,
+ ParsableForStrings parsableForStrings,
+ ParsableForPrimitives parsableForPrimitives,
+ TryFromGeneration tryFromGeneration,
+ IsInitializedMethodGeneration isInitializedMethodGeneration,
+ PrimitiveEqualityGeneration primitiveEqualityGeneration,
+ NumericsGeneration numericsGeneration,
+ StringComparisonDefault stringDefaultComparison)
+ {
+ // DO NOT ADD PARAMETERS HERE, INSTEAD, CREATE OVERLOADS (at least until a new major version).
+ // This is because some users use reflection to find this attribute, and changing the amount
+ // of parameters is a binary-breaking change. See https://github.com/dotnet/runtime/issues/103722
+ // for more information.
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Vogen.SharedTypes/VogenDefaultsAttribute.cs b/src/Vogen.SharedTypes/VogenDefaultsAttribute.cs
index 1990470c41d..2c4aeed3695 100644
--- a/src/Vogen.SharedTypes/VogenDefaultsAttribute.cs
+++ b/src/Vogen.SharedTypes/VogenDefaultsAttribute.cs
@@ -1,4 +1,4 @@
-// ReSharper disable MemberInitializerValueIgnored
+// ReSharper disable MemberInitializerValueIgnored
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedParameter.Local
@@ -76,4 +76,68 @@ public VogenDefaultsAttribute(
NumericsGeneration numericsGeneration = NumericsGeneration.Unspecified)
{
}
+
+ ///
+ /// Creates a new instance of a type that represents the default
+ /// values used for value object generation.
+ ///
+ /// The type of the primitive that is being wrapped—defaults to int.
+ /// Specifies what conversion code is generated - defaults to which generates type converters and a converter to handle serialization using System.Text.Json
+ /// Specifies the type of exception thrown when validation fails—defaults to .
+ /// Simple customization switches—defaults to .
+ /// Specifies how strict deserialization is, e.g. should your Validate method be called, or should pre-defined instances that otherwise invalid be allowed - defaults to .
+ /// Specifies the level that debug attributes are written as some IDEs don't support all of them, e.g., Rider - defaults to which generates DebuggerDisplay and a debugger proxy type for IDEs that support them
+ /// Controls how cast operators are generated for casting from the Value Object to the primitive.
+ /// Options are implicit or explicit or none. Explicit is preferred over implicit if you really need them, but isn't recommended.
+ /// See for more information.
+ /// Controls how cast operators are generated for casting from the primitive to the Value Object.
+ /// Options are implicit or explicit or none. Explicit is preferred over implicit if you really need them, but isn't recommended.
+ /// See <see href="https://github.com/SteveDunn/Vogen/wiki/Casting"/> for more information.
+ /// disables stack trace recording; in Debug builds, a stack trace is recorded and is
+ /// thrown in the exception when something is created in an uninitialized state, e.g. after deserialization
+ /// Specifies what is generated for IParsable types for strings - defaults to .
+ /// Specifies what is generated for Parse and TryParse methods - defaults to .
+ /// Specifies what to write for TryFrom methods—defaults to .
+ /// Specifies whether to generate an IsInitialized() method - defaults to .
+ ///
+ /// Specifies whether to generate primitive comparison operators, allowing this type to be compared for equality to the primitive.
+ /// Defaults to
+ ///
+ ///
+ /// var vo = MyInt.From(123);
+ ///
+ ///
+ /// bool same = vo == 123;
+ ///
+ ///
+ ///
+ /// Controls the generation of the type factory for System.Text.Json.
+ /// Controls the generation of static abstract interfaces.
+ /// Controls the generation of a Swashbuckle schema filter for OpenAPI.
+ /// Every ValueObject attribute must explicitly specify the type of the primitive.
+ /// Specifies whether to generate numeric interfaces (INumber<T> or INumberBase<T> depending on the underlying type)—defaults to .
+ /// Specifies the default used for ==, Equals, and GetHashCode on string-backed value objects—defaults to which uses the underlying string's default comparison.
+ public VogenDefaultsAttribute(
+ Type? underlyingType,
+ Conversions conversions,
+ Type? throws,
+ Customizations customizations,
+ DeserializationStrictness deserializationStrictness,
+ DebuggerAttributeGeneration debuggerAttributes,
+ CastOperator toPrimitiveCasting,
+ CastOperator fromPrimitiveCasting,
+ bool disableStackTraceRecordingInDebug,
+ ParsableForStrings parsableForStrings,
+ ParsableForPrimitives parsableForPrimitives,
+ TryFromGeneration tryFromGeneration,
+ IsInitializedMethodGeneration isInitializedMethodGeneration,
+ SystemTextJsonConverterFactoryGeneration systemTextJsonConverterFactoryGeneration,
+ StaticAbstractsGeneration staticAbstractsGeneration,
+ OpenApiSchemaCustomizations openApiSchemaCustomizations,
+ bool explicitlySpecifyTypeInValueObject,
+ PrimitiveEqualityGeneration primitiveEqualityGeneration,
+ NumericsGeneration numericsGeneration,
+ StringComparisonDefault stringDefaultComparison)
+ {
+ }
}
\ No newline at end of file
diff --git a/src/Vogen/BuildConfigurationFromAttributes.cs b/src/Vogen/BuildConfigurationFromAttributes.cs
index aadea943f34..6466c06ffa4 100644
--- a/src/Vogen/BuildConfigurationFromAttributes.cs
+++ b/src/Vogen/BuildConfigurationFromAttributes.cs
@@ -38,6 +38,7 @@ internal class BuildConfigurationFromAttributes
private bool _primitiveTypeMustBeExplicit;
private PrimitiveEqualityGeneration _primitiveEqualityGeneration;
private NumericsGeneration _numericsGeneration;
+ private StringComparisonDefault _stringDefaultComparison;
private BuildConfigurationFromAttributes(AttributeData att)
{
@@ -64,6 +65,7 @@ private BuildConfigurationFromAttributes(AttributeData att)
_primitiveTypeMustBeExplicit = false;
_primitiveEqualityGeneration = PrimitiveEqualityGeneration.Unspecified;
_numericsGeneration = NumericsGeneration.Unspecified;
+ _stringDefaultComparison = StringComparisonDefault.Unspecified;
_diagnostics = new List();
@@ -127,7 +129,8 @@ private VogenConfigurationBuildResult Build(bool argsAreFromVogenDefaultAttribut
OpenApiSchemaCustomizations: _openApiSchemaCustomizations,
ExplicitlySpecifyTypeInValueObject: _primitiveTypeMustBeExplicit,
PrimitiveEqualityGeneration: _primitiveEqualityGeneration,
- NumericsGeneration: _numericsGeneration),
+ NumericsGeneration: _numericsGeneration,
+ StringDefaultComparison: _stringDefaultComparison),
diagnostics: _diagnostics);
}
@@ -194,6 +197,7 @@ private void PopulateDiagnosticsWithAnyValidationIssues()
{
_diagnostics.Add(DiagnosticsCatalogue.InvalidDeserializationStrictness(syntaxLocation));
}
+
}
// populates all args - it doesn't expect the underlying type argument as that is:
@@ -202,7 +206,7 @@ private void PopulateDiagnosticsWithAnyValidationIssues()
// ReSharper disable once CognitiveComplexity
private void PopulateFromVogenDefaultsAttributeArgs(ImmutableArray argsExcludingUnderlyingType)
{
- if (argsExcludingUnderlyingType.Length > 18)
+ if (argsExcludingUnderlyingType.Length > 19)
{
throw new InvalidOperationException("Too many arguments for the attribute.");
}
@@ -216,6 +220,11 @@ private void PopulateFromVogenDefaultsAttributeArgs(ImmutableArray args)
{
- if (args.Length > 15)
+ if (args.Length > 16)
{
throw new InvalidOperationException("Too many arguments for the attribute.");
}
@@ -327,6 +336,11 @@ private void PopulateFromValueObjectAttributeArgs(ImmutableArray
continue;
}
+ if (i == 15)
+ {
+ _stringDefaultComparison = (StringComparisonDefault) v;
+ }
+
if (i == 14)
{
_numericsGeneration = (NumericsGeneration) v;
diff --git a/src/Vogen/BuildWorkItems.cs b/src/Vogen/BuildWorkItems.cs
index 36a5a59c0a1..e719a8b4678 100644
--- a/src/Vogen/BuildWorkItems.cs
+++ b/src/Vogen/BuildWorkItems.cs
@@ -99,6 +99,14 @@ internal static class BuildWorkItems
}
}
+ bool hasActiveDefaultComparison = config.StringDefaultComparison is not StringComparisonDefault.Unspecified
+ and not StringComparisonDefault.Omit;
+
+ if (hasActiveDefaultComparison && underlyingType.SpecialType != Microsoft.CodeAnalysis.SpecialType.System_String)
+ {
+ context.ReportDiagnostic(DiagnosticsCatalogue.StringDefaultComparisonNotApplicable(voSymbolInformation, underlyingType));
+ }
+
IEnumerable instanceProperties =
TryBuildInstanceProperties(allAttributes, voSymbolInformation, context, underlyingType, vogenKnownSymbols).ToList();
diff --git a/src/Vogen/CombineConfigurations.cs b/src/Vogen/CombineConfigurations.cs
index 4f882cc9699..6f30fbca5b6 100644
--- a/src/Vogen/CombineConfigurations.cs
+++ b/src/Vogen/CombineConfigurations.cs
@@ -158,6 +158,14 @@ public static VogenConfiguration CombineAndResolveAnythingUnspecified(
(var local, _) => local,
};
+ StringComparisonDefault stringDefaultComparison = (localValues.StringDefaultComparison, globalValues?.StringDefaultComparison) switch
+ {
+ (StringComparisonDefault.Unspecified, null) => VogenConfiguration.DefaultInstance.StringDefaultComparison,
+ (StringComparisonDefault.Unspecified, StringComparisonDefault.Unspecified) => VogenConfiguration.DefaultInstance.StringDefaultComparison,
+ (StringComparisonDefault.Unspecified, var global) => global.Value,
+ (var local, _) => local,
+ };
+
var validationExceptionType = localValues.ValidationExceptionType ??
globalValues?.ValidationExceptionType ??
VogenConfiguration.DefaultInstance.ValidationExceptionType;
@@ -193,7 +201,8 @@ public static VogenConfiguration CombineAndResolveAnythingUnspecified(
OpenApiSchemaCustomizations: openApiSchemaCustomizations,
ExplicitlySpecifyTypeInValueObject: primitiveTypeMustBeExplicit,
PrimitiveEqualityGeneration: primitiveEqualityGeneration,
- NumericsGeneration: numericsGeneration);
+ NumericsGeneration: numericsGeneration,
+ StringDefaultComparison: stringDefaultComparison);
}
/// If we don't have a global attribute, just use the default configuration as there
diff --git a/src/Vogen/Diagnostics/DiagnosticsCatalogue.cs b/src/Vogen/Diagnostics/DiagnosticsCatalogue.cs
index 0dba663c75b..6d1db423dd6 100644
--- a/src/Vogen/Diagnostics/DiagnosticsCatalogue.cs
+++ b/src/Vogen/Diagnostics/DiagnosticsCatalogue.cs
@@ -135,6 +135,12 @@ internal static class DiagnosticsCatalogue
"'{0}' has NumericsGeneration.Generate set, but the underlying type '{1}' does not implement INumberBase. No numeric interface will be generated.",
DiagnosticSeverity.Warning);
+ private static readonly DiagnosticDescriptor _stringDefaultComparisonNotApplicable = CreateDescriptor(
+ RuleIdentifiers.StringDefaultComparisonNotApplicable,
+ "StringDefaultComparison has no effect",
+ "'{0}' has StringDefaultComparison set, but the underlying type '{1}' is not a string. StringDefaultComparison only applies to string-backed value objects.",
+ DiagnosticSeverity.Warning);
+
public static Diagnostic TypeCannotBeNested(INamedTypeSymbol typeModel, INamedTypeSymbol container) =>
Create(_typeCannotBeNested, typeModel.Locations, typeModel.Name, container.Name);
@@ -214,6 +220,9 @@ public static Diagnostic TypesReferencedInAConversionMarkerMustBeValueObjects(IN
public static Diagnostic NumericsGenerationNotApplicable(INamedTypeSymbol voSymbol, ITypeSymbol underlyingType) =>
Create(_numericsGenerationNotApplicable, voSymbol.Locations, voSymbol.Name, underlyingType.Name);
+ public static Diagnostic StringDefaultComparisonNotApplicable(INamedTypeSymbol voSymbol, ITypeSymbol underlyingType) =>
+ Create(_stringDefaultComparisonNotApplicable, voSymbol.Locations, voSymbol.Name, underlyingType.Name);
+
private static DiagnosticDescriptor CreateDescriptor(string code, string title, string messageFormat, DiagnosticSeverity severity = DiagnosticSeverity.Error)
{
string[] tags = severity == DiagnosticSeverity.Error ? new[] { WellKnownDiagnosticTags.NotConfigurable } : Array.Empty();
diff --git a/src/Vogen/Diagnostics/RuleIdentifiers.cs b/src/Vogen/Diagnostics/RuleIdentifiers.cs
index d58182018f0..d4b5bb8687c 100644
--- a/src/Vogen/Diagnostics/RuleIdentifiers.cs
+++ b/src/Vogen/Diagnostics/RuleIdentifiers.cs
@@ -45,4 +45,5 @@ public static class RuleIdentifiers
public const string BothImplicitAndExplicitCastsSpecified = "VOG036";
public const string NumericsGenerationNotApplicable = "VOG037";
public const string PropertyOfValueObjectShouldBeInitialized = "VOG038";
+ public const string StringDefaultComparisonNotApplicable = "VOG039";
}
\ No newline at end of file
diff --git a/src/Vogen/GenerateCodeForEqualsMethodsAndOperators.cs b/src/Vogen/GenerateCodeForEqualsMethodsAndOperators.cs
index bdc70270fbc..857a2e8afb9 100644
--- a/src/Vogen/GenerateCodeForEqualsMethodsAndOperators.cs
+++ b/src/Vogen/GenerateCodeForEqualsMethodsAndOperators.cs
@@ -13,7 +13,11 @@ public static string GenerateEqualsMethodsForAClass(VoWorkItem item, TypeDeclara
string virtualKeyword = item is { IsRecordClass: true, IsSealed: false } ? "virtual " : " ";
- var ret = item.UserProvidedOverloads.EqualsForWrapper.WasProvided ? string.Empty :
+ string classEqualityExpr = item.IsUnderlyingAString && item.Config.GetStringDefaultComparerExpression() is { } classComparer
+ ? $"{classComparer}.Equals(Value, other.Value)"
+ : $"global::System.Collections.Generic.EqualityComparer<{item.UnderlyingTypeFullNameWithGlobalAlias}>.Default.Equals(Value, other.Value)";
+
+ var ret = item.UserProvidedOverloads.EqualsForWrapper.WasProvided ? string.Empty :
$$"""
public {{virtualKeyword}}global::System.Boolean Equals({{className}}{{wrapperQ}} other)
@@ -32,7 +36,7 @@ public static string GenerateEqualsMethodsForAClass(VoWorkItem item, TypeDeclara
return true;
}
- return GetType() == other.GetType() && {{$"global::System.Collections.Generic.EqualityComparer<{item.UnderlyingTypeFullNameWithGlobalAlias}>.Default.Equals(Value, other.Value)"}};
+ return GetType() == other.GetType() && {{classEqualityExpr}};
}
""";
@@ -65,6 +69,10 @@ public static string GenerateEqualsMethodsForAStruct(VoWorkItem item, TypeDeclar
{
var structName = tds.Identifier;
+ string structEqualityExpr = item.IsUnderlyingAString && item.Config.GetStringDefaultComparerExpression() is { } structComparer
+ ? $"{structComparer}.Equals(Value, other.Value)"
+ : $"global::System.Collections.Generic.EqualityComparer<{item.UnderlyingTypeFullNameWithGlobalAlias}>.Default.Equals(Value, other.Value)";
+
var ret = item.UserProvidedOverloads.EqualsForWrapper.WasProvided ? string.Empty :
$$"""
public readonly global::System.Boolean Equals({{structName}} other)
@@ -73,7 +81,7 @@ public static string GenerateEqualsMethodsForAStruct(VoWorkItem item, TypeDeclar
// We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type.
if(!IsInitialized() || !other.IsInitialized()) return false;
- return {{$"global::System.Collections.Generic.EqualityComparer<{item.UnderlyingTypeFullNameWithGlobalAlias}>.Default.Equals(Value, other.Value)"}};
+ return {{structEqualityExpr}};
}
""";
@@ -118,12 +126,16 @@ private static string GenerateEqualsMethodForPrimitive(VoWorkItem item, bool isR
string readonlyOrEmpty = isReadOnly ? " readonly" : string.Empty;
+ string primitiveEqualityBody = isString && item.Config.GetStringDefaultComparerExpression() is { } primComparer
+ ? $"return {primComparer}.Equals(Value, primitive);"
+ : "return Value.Equals(primitive);";
+
string output = item.UserProvidedOverloads.EqualsForUnderlying.WasProvided ? string.Empty :
$$"""
public{{readonlyOrEmpty}} global::System.Boolean Equals({{item.UnderlyingTypeFullNameWithGlobalAlias}}{{underlyingQ}} primitive)
{
- return Value.Equals(primitive);
+ {{primitiveEqualityBody}}
}
""";
@@ -152,11 +164,23 @@ public static string GenerateEqualsOperatorsForPrimitivesIfNeeded(string itemUnd
string w = item.Nullable.QuestionMarkForWrapper;
string u = item.Nullable.QuestionMarkForUnderlying;
-
+
+ if (item.IsUnderlyingAString && item.Config.GetStringDefaultComparerExpression() is { } opComparer)
+ {
+ return $"""
+
+ public static global::System.Boolean operator ==({typeName}{w} left, {itemUnderlyingType}{u} right) => {opComparer}.Equals(left{w}.Value, right);
+ public static global::System.Boolean operator ==({itemUnderlyingType}{u} left, {typeName}{w} right) => {opComparer}.Equals(left, right{w}.Value);
+
+ public static global::System.Boolean operator !=({itemUnderlyingType}{u} left, {typeName}{w} right) => !(left == right);
+ public static global::System.Boolean operator !=({typeName}{w} left, {itemUnderlyingType}{u} right) => !(left == right);
+ """;
+ }
+
string wDefaultToFalse = item.Nullable.IsEnabled && item.IsTheWrapperAReferenceType ? "?? false" : string.Empty;
return $"""
-
+
public static global::System.Boolean operator ==({typeName}{w} left, {itemUnderlyingType}{u} right) => left{w}.Value.Equals(right) {wDefaultToFalse};
public static global::System.Boolean operator ==({itemUnderlyingType}{u} left, {typeName}{w} right) => right{w}.Value.Equals(left) {wDefaultToFalse};
diff --git a/src/Vogen/GenerateCodeForHashCodes.cs b/src/Vogen/GenerateCodeForHashCodes.cs
index bdea6ceb97f..c6d6fc97dd5 100644
--- a/src/Vogen/GenerateCodeForHashCodes.cs
+++ b/src/Vogen/GenerateCodeForHashCodes.cs
@@ -16,13 +16,15 @@ public static string GenerateGetHashCodeForAClass(VoWorkItem item)
// When the underlying type is a nullable value type (e.g. ushort?), EqualityComparer.Default.GetHashCode
// has [DisallowNull] on its parameter in .NET 10+, causing CS8607. Use null-safe approach instead.
// Wrap the expression in parentheses to ensure correct precedence with the ^ operator.
- string hashCodeExpression = item.UnderlyingType.IsNullableValueType()
- ? "(Value is null ? 0 : Value.GetHashCode())"
- : $"global::System.Collections.Generic.EqualityComparer<{itemUnderlyingType}>.Default.GetHashCode(Value)";
+ string hashCodeExpression = item.IsUnderlyingAString && item.Config.GetStringDefaultComparerExpression() is { } classHashComparer
+ ? $"{classHashComparer}.GetHashCode(Value)"
+ : item.UnderlyingType.IsNullableValueType()
+ ? "(Value is null ? 0 : Value.GetHashCode())"
+ : $"global::System.Collections.Generic.EqualityComparer<{itemUnderlyingType}>.Default.GetHashCode(Value)";
return
$$"""
-
+
public override global::System.Int32 GetHashCode()
{
unchecked // Overflow is fine, just wrap
@@ -47,13 +49,15 @@ public static string GenerateForAStruct(VoWorkItem item)
// When the underlying type is a nullable value type (e.g. ushort?), EqualityComparer.Default.GetHashCode
// has [DisallowNull] on its parameter in .NET 10+, causing CS8607. Use null-safe approach instead.
- string hashCodeExpression = item.UnderlyingType.IsNullableValueType()
- ? "Value is null ? 0 : Value.GetHashCode()"
- : $"global::System.Collections.Generic.EqualityComparer<{itemUnderlyingType}>.Default.GetHashCode(Value)";
+ string hashCodeExpression = item.IsUnderlyingAString && item.Config.GetStringDefaultComparerExpression() is { } structHashComparer
+ ? $"{structHashComparer}.GetHashCode(Value)"
+ : item.UnderlyingType.IsNullableValueType()
+ ? "Value is null ? 0 : Value.GetHashCode()"
+ : $"global::System.Collections.Generic.EqualityComparer<{itemUnderlyingType}>.Default.GetHashCode(Value)";
return
$$"""
-
+
public readonly override global::System.Int32 GetHashCode()
{
return {{hashCodeExpression}};
diff --git a/src/Vogen/GenerateCodeForStringComparers.cs b/src/Vogen/GenerateCodeForStringComparers.cs
index 0a57885290f..52efe9558cc 100644
--- a/src/Vogen/GenerateCodeForStringComparers.cs
+++ b/src/Vogen/GenerateCodeForStringComparers.cs
@@ -11,7 +11,7 @@ public static string GenerateIfNeeded(VoWorkItem item, TypeDeclarationSyntax tds
return string.Empty;
}
- if(item.Config.StringComparers != StringComparersGeneration.Generate)
+ if (item.Config.StringComparers != StringComparersGeneration.Generate)
{
return string.Empty;
}
@@ -35,9 +35,9 @@ public bool Equals({{tds.Identifier}} x, {{tds.Identifier}} y)
return _comparer.Equals(x._value, y._value);
}
- public int GetHashCode({{tds.Identifier}} obj)
+ public int GetHashCode({{tds.Identifier}} obj)
{
- return _comparer.GetHashCode();
+ return _comparer.GetHashCode(obj._value);
}
}
diff --git a/src/Vogen/VogenConfiguration.cs b/src/Vogen/VogenConfiguration.cs
index 41d052c7ecc..c031bf5ce5d 100644
--- a/src/Vogen/VogenConfiguration.cs
+++ b/src/Vogen/VogenConfiguration.cs
@@ -23,7 +23,8 @@ public record VogenConfiguration(
OpenApiSchemaCustomizations OpenApiSchemaCustomizations,
bool ExplicitlySpecifyTypeInValueObject,
PrimitiveEqualityGeneration PrimitiveEqualityGeneration,
- NumericsGeneration NumericsGeneration)
+ NumericsGeneration NumericsGeneration,
+ StringComparisonDefault StringDefaultComparison)
{
// Don't add default values here, they should be in DefaultInstance.
@@ -52,5 +53,17 @@ public record VogenConfiguration(
OpenApiSchemaCustomizations: OpenApiSchemaCustomizations.Omit,
ExplicitlySpecifyTypeInValueObject: false,
PrimitiveEqualityGeneration: PrimitiveEqualityGeneration.GenerateOperatorsAndMethods,
- NumericsGeneration: NumericsGeneration.Omit);
+ NumericsGeneration: NumericsGeneration.Omit,
+ StringDefaultComparison: StringComparisonDefault.Omit);
+
+ public string? GetStringDefaultComparerExpression() => StringDefaultComparison switch
+ {
+ StringComparisonDefault.Ordinal => "global::System.StringComparer.Ordinal",
+ StringComparisonDefault.OrdinalIgnoreCase => "global::System.StringComparer.OrdinalIgnoreCase",
+ StringComparisonDefault.CurrentCulture => "global::System.StringComparer.CurrentCulture",
+ StringComparisonDefault.CurrentCultureIgnoreCase => "global::System.StringComparer.CurrentCultureIgnoreCase",
+ StringComparisonDefault.InvariantCulture => "global::System.StringComparer.InvariantCulture",
+ StringComparisonDefault.InvariantCultureIgnoreCase => "global::System.StringComparer.InvariantCultureIgnoreCase",
+ _ => null
+ };
}
diff --git a/tests/ConsumerTests/StringComparisons/ForClasses.cs b/tests/ConsumerTests/StringComparisons/ForClasses.cs
index 106cf2b3bbb..d43bfec78b0 100644
--- a/tests/ConsumerTests/StringComparisons/ForClasses.cs
+++ b/tests/ConsumerTests/StringComparisons/ForClasses.cs
@@ -55,6 +55,24 @@ public void OrdinalIgnoreCase_Generic()
(left == right).Should().BeFalse();
}
+ [Fact]
+ public void StringDefaultComparison_OrdinalIgnoreCase()
+ {
+ using var _ = new AssertionScope();
+
+ StringVo_Class_DefaultOrdinalIgnoreCase left = StringVo_Class_DefaultOrdinalIgnoreCase.From("abc");
+ StringVo_Class_DefaultOrdinalIgnoreCase right = StringVo_Class_DefaultOrdinalIgnoreCase.From("ABC");
+
+ (left == right).Should().BeTrue();
+ left.Equals(right).Should().BeTrue();
+ left.Equals("ABC").Should().BeTrue();
+ left.GetHashCode().Should().Be(right.GetHashCode());
+
+ Dictionary d = new();
+ d.Add(StringVo_Class_DefaultOrdinalIgnoreCase.From("one"), 1);
+ d.ContainsKey(StringVo_Class_DefaultOrdinalIgnoreCase.From("ONE")).Should().BeTrue();
+ }
+
[Fact]
public void OrdinalIgnoreCase_in_a_dictionary()
{
diff --git a/tests/ConsumerTests/StringComparisons/ForRecordClasses.cs b/tests/ConsumerTests/StringComparisons/ForRecordClasses.cs
index 6786b3e5c72..9d59346ea87 100644
--- a/tests/ConsumerTests/StringComparisons/ForRecordClasses.cs
+++ b/tests/ConsumerTests/StringComparisons/ForRecordClasses.cs
@@ -55,6 +55,24 @@ public void OrdinalIgnoreCase_Generic()
(left == right).Should().BeFalse();
}
+ [Fact]
+ public void StringDefaultComparison_OrdinalIgnoreCase()
+ {
+ using var _ = new AssertionScope();
+
+ StringVo_RecordClass_DefaultOrdinalIgnoreCase left = StringVo_RecordClass_DefaultOrdinalIgnoreCase.From("abc");
+ StringVo_RecordClass_DefaultOrdinalIgnoreCase right = StringVo_RecordClass_DefaultOrdinalIgnoreCase.From("ABC");
+
+ (left == right).Should().BeTrue();
+ left.Equals(right).Should().BeTrue();
+ left.Equals("ABC").Should().BeTrue();
+ left.GetHashCode().Should().Be(right.GetHashCode());
+
+ Dictionary d = new();
+ d.Add(StringVo_RecordClass_DefaultOrdinalIgnoreCase.From("one"), 1);
+ d.ContainsKey(StringVo_RecordClass_DefaultOrdinalIgnoreCase.From("ONE")).Should().BeTrue();
+ }
+
[Fact]
public void OrdinalIgnoreCase_in_a_dictionary()
{
diff --git a/tests/ConsumerTests/StringComparisons/ForRecordStructs.cs b/tests/ConsumerTests/StringComparisons/ForRecordStructs.cs
index d949613fc44..b91b5de424b 100644
--- a/tests/ConsumerTests/StringComparisons/ForRecordStructs.cs
+++ b/tests/ConsumerTests/StringComparisons/ForRecordStructs.cs
@@ -58,6 +58,24 @@ public void OrdinalIgnoreCase_Generic()
(left == right).Should().BeFalse();
}
+ [Fact]
+ public void StringDefaultComparison_OrdinalIgnoreCase()
+ {
+ using var _ = new AssertionScope();
+
+ StringVo_RecordStruct_DefaultOrdinalIgnoreCase left = StringVo_RecordStruct_DefaultOrdinalIgnoreCase.From("abc");
+ StringVo_RecordStruct_DefaultOrdinalIgnoreCase right = StringVo_RecordStruct_DefaultOrdinalIgnoreCase.From("ABC");
+
+ (left == right).Should().BeTrue();
+ left.Equals(right).Should().BeTrue();
+ left.Equals("ABC").Should().BeTrue();
+ left.GetHashCode().Should().Be(right.GetHashCode());
+
+ Dictionary d = new();
+ d.Add(StringVo_RecordStruct_DefaultOrdinalIgnoreCase.From("one"), 1);
+ d.ContainsKey(StringVo_RecordStruct_DefaultOrdinalIgnoreCase.From("ONE")).Should().BeTrue();
+ }
+
[Fact]
public void OrdinalIgnoreCase_in_a_dictionary()
{
diff --git a/tests/ConsumerTests/StringComparisons/ForStructs.cs b/tests/ConsumerTests/StringComparisons/ForStructs.cs
index 0d29d5debc6..f8676bebf25 100644
--- a/tests/ConsumerTests/StringComparisons/ForStructs.cs
+++ b/tests/ConsumerTests/StringComparisons/ForStructs.cs
@@ -71,6 +71,24 @@ public void OrdinalIgnoreCase_Generic()
(left == right).Should().BeFalse();
}
+ [Fact]
+ public void StringDefaultComparison_OrdinalIgnoreCase()
+ {
+ using var _ = new AssertionScope();
+
+ StringVo_Struct_DefaultOrdinalIgnoreCase left = StringVo_Struct_DefaultOrdinalIgnoreCase.From("abc");
+ StringVo_Struct_DefaultOrdinalIgnoreCase right = StringVo_Struct_DefaultOrdinalIgnoreCase.From("ABC");
+
+ (left == right).Should().BeTrue();
+ left.Equals(right).Should().BeTrue();
+ left.Equals("ABC").Should().BeTrue();
+ left.GetHashCode().Should().Be(right.GetHashCode());
+
+ Dictionary d = new();
+ d.Add(StringVo_Struct_DefaultOrdinalIgnoreCase.From("one"), 1);
+ d.ContainsKey(StringVo_Struct_DefaultOrdinalIgnoreCase.From("ONE")).Should().BeTrue();
+ }
+
[Fact]
public void OrdinalIgnoreCase_in_a_dictionary()
{
diff --git a/tests/ConsumerTests/StringComparisons/Types.cs b/tests/ConsumerTests/StringComparisons/Types.cs
index cbf16506b24..717ee9feafa 100644
--- a/tests/ConsumerTests/StringComparisons/Types.cs
+++ b/tests/ConsumerTests/StringComparisons/Types.cs
@@ -90,3 +90,23 @@ public partial record struct StringVo_RecordStruct_Generic
public partial struct StringVo_Struct_Generic
{
}
+
+[ValueObject(typeof(string), Conversions.Default, null, Customizations.None, DeserializationStrictness.AllowValidAndKnownInstances, DebuggerAttributeGeneration.Default, ComparisonGeneration.Default, StringComparersGeneration.Unspecified, CastOperator.Unspecified, CastOperator.Unspecified, ParsableForStrings.Unspecified, ParsableForPrimitives.Unspecified, TryFromGeneration.Unspecified, IsInitializedMethodGeneration.Unspecified, PrimitiveEqualityGeneration.Unspecified, NumericsGeneration.Unspecified, StringComparisonDefault.OrdinalIgnoreCase)]
+public partial class StringVo_Class_DefaultOrdinalIgnoreCase
+{
+}
+
+[ValueObject(typeof(string), Conversions.Default, null, Customizations.None, DeserializationStrictness.AllowValidAndKnownInstances, DebuggerAttributeGeneration.Default, ComparisonGeneration.Default, StringComparersGeneration.Unspecified, CastOperator.Unspecified, CastOperator.Unspecified, ParsableForStrings.Unspecified, ParsableForPrimitives.Unspecified, TryFromGeneration.Unspecified, IsInitializedMethodGeneration.Unspecified, PrimitiveEqualityGeneration.Unspecified, NumericsGeneration.Unspecified, StringComparisonDefault.OrdinalIgnoreCase)]
+public partial record class StringVo_RecordClass_DefaultOrdinalIgnoreCase
+{
+}
+
+[ValueObject(typeof(string), Conversions.Default, null, Customizations.None, DeserializationStrictness.AllowValidAndKnownInstances, DebuggerAttributeGeneration.Default, ComparisonGeneration.Default, StringComparersGeneration.Unspecified, CastOperator.Unspecified, CastOperator.Unspecified, ParsableForStrings.Unspecified, ParsableForPrimitives.Unspecified, TryFromGeneration.Unspecified, IsInitializedMethodGeneration.Unspecified, PrimitiveEqualityGeneration.Unspecified, NumericsGeneration.Unspecified, StringComparisonDefault.OrdinalIgnoreCase)]
+public partial record struct StringVo_RecordStruct_DefaultOrdinalIgnoreCase
+{
+}
+
+[ValueObject(typeof(string), Conversions.Default, null, Customizations.None, DeserializationStrictness.AllowValidAndKnownInstances, DebuggerAttributeGeneration.Default, ComparisonGeneration.Default, StringComparersGeneration.Unspecified, CastOperator.Unspecified, CastOperator.Unspecified, ParsableForStrings.Unspecified, ParsableForPrimitives.Unspecified, TryFromGeneration.Unspecified, IsInitializedMethodGeneration.Unspecified, PrimitiveEqualityGeneration.Unspecified, NumericsGeneration.Unspecified, StringComparisonDefault.OrdinalIgnoreCase)]
+public partial struct StringVo_Struct_DefaultOrdinalIgnoreCase
+{
+}
diff --git a/tests/SnapshotTests/StringComparison/StringComparisonGenerationTests.cs b/tests/SnapshotTests/StringComparison/StringComparisonGenerationTests.cs
index 26d7003cb35..f199c11a0dd 100644
--- a/tests/SnapshotTests/StringComparison/StringComparisonGenerationTests.cs
+++ b/tests/SnapshotTests/StringComparison/StringComparisonGenerationTests.cs
@@ -39,6 +39,40 @@ public partial class StringThing { }
.RunOnAllFrameworks();
}
+ [Fact]
+ public Task Generates_with_default_comparison_OrdinalIgnoreCase()
+ {
+ string source = $$"""
+ using System;
+ using Vogen;
+ namespace Whatever;
+
+ [ValueObject(typeof(string), Conversions.Default, null, Customizations.None, DeserializationStrictness.AllowValidAndKnownInstances, DebuggerAttributeGeneration.Default, ComparisonGeneration.Default, StringComparersGeneration.Unspecified, CastOperator.Unspecified, CastOperator.Unspecified, ParsableForStrings.Unspecified, ParsableForPrimitives.Unspecified, TryFromGeneration.Unspecified, IsInitializedMethodGeneration.Unspecified, PrimitiveEqualityGeneration.Unspecified, NumericsGeneration.Unspecified, StringComparisonDefault.OrdinalIgnoreCase)]
+ public partial class StringThing { }
+ """;
+
+ return new SnapshotRunner()
+ .WithSource(source)
+ .RunOnAllFrameworks();
+ }
+
+ [Fact]
+ public Task Generates_comparers_class_with_default_comparison_when_both_are_specified()
+ {
+ string source = $$"""
+ using System;
+ using Vogen;
+ namespace Whatever;
+
+ [ValueObject(typeof(string), Conversions.Default, null, Customizations.None, DeserializationStrictness.AllowValidAndKnownInstances, DebuggerAttributeGeneration.Default, ComparisonGeneration.Default, StringComparersGeneration.Generate, CastOperator.Unspecified, CastOperator.Unspecified, ParsableForStrings.Unspecified, ParsableForPrimitives.Unspecified, TryFromGeneration.Unspecified, IsInitializedMethodGeneration.Unspecified, PrimitiveEqualityGeneration.Unspecified, NumericsGeneration.Unspecified, StringComparisonDefault.OrdinalIgnoreCase)]
+ public partial class StringThing { }
+ """;
+
+ return new SnapshotRunner()
+ .WithSource(source)
+ .RunOnAllFrameworks();
+ }
+
[Fact]
public Task Does_not_generate_the_equals_method_that_takes_a_StringComparison_when_not_a_string()
{
diff --git a/tests/SnapshotTests/StringComparison/snapshots/snap-v4.8/StringComparisonGenerationTests.Generates_comparers_class_with_default_comparison_when_both_are_specified.verified.txt b/tests/SnapshotTests/StringComparison/snapshots/snap-v4.8/StringComparisonGenerationTests.Generates_comparers_class_with_default_comparison_when_both_are_specified.verified.txt
new file mode 100644
index 00000000000..cea860f8bfe
--- /dev/null
+++ b/tests/SnapshotTests/StringComparison/snapshots/snap-v4.8/StringComparisonGenerationTests.Generates_comparers_class_with_default_comparison_when_both_are_specified.verified.txt
@@ -0,0 +1,620 @@
+[
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen)
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+// Suppress warnings for 'Override methods on comparable types'.
+#pragma warning disable CA1036
+// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators
+#pragma warning disable MA0097
+// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.'
+// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations.
+#pragma warning disable CS8669, CS8632
+#pragma warning disable CS8604 // Possible null reference argument.
+
+// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member'
+#pragma warning disable CS1591
+// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
+#pragma warning disable CS8767
+namespace generator
+{
+ [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")]
+ public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory
+ {
+ public VogenTypesFactory()
+ {
+ }
+
+ private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary>
+ {
+ {
+ typeof(global::Whatever.StringThing),
+ new global::System.Lazy(() => new global::Whatever.StringThing.StringThingSystemTextJsonConverter())
+ }
+ };
+ public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert);
+ public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value;
+ }
+}
+
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen)
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+// Suppress warnings for 'Override methods on comparable types'.
+#pragma warning disable CA1036
+// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators
+#pragma warning disable MA0097
+// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.'
+// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations.
+#pragma warning disable CS8669, CS8632
+#pragma warning disable CS8604 // Possible null reference argument.
+
+// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member'
+#pragma warning disable CS1591
+// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
+#pragma warning disable CS8767
+namespace Whatever
+{
+ [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")]
+ [global::System.Text.Json.Serialization.JsonConverter(typeof(StringThingSystemTextJsonConverter))]
+ [global::System.ComponentModel.TypeConverter(typeof(StringThingTypeConverter))]
+ [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(StringThingDebugView))]
+ [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.String, Value = { _value }")]
+ public partial class StringThing : global::System.IEquatable, global::System.IEquatable, global::System.IComparable, global::System.IComparable, global::System.IConvertible
+ {
+#if DEBUG
+private readonly global::System.Diagnostics.StackTrace _stackTrace = null!;
+#endif
+#if !VOGEN_NO_VALIDATION
+ private readonly global::System.Boolean _isInitialized;
+#endif
+ private readonly global::System.String _value;
+ ///
+ /// Gets the underlying value if set, otherwise a is thrown.
+ ///
+ public global::System.String Value
+ {
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ EnsureInitialized();
+ return _value;
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ public StringThing()
+ {
+#if DEBUG
+ _stackTrace = new global::System.Diagnostics.StackTrace();
+#endif
+#if !VOGEN_NO_VALIDATION
+ _isInitialized = false;
+#endif
+ _value = default;
+ }
+
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ private StringThing(global::System.String value)
+ {
+ _value = value;
+#if !VOGEN_NO_VALIDATION
+ _isInitialized = true;
+#endif
+ }
+
+ ///
+ /// Builds an instance from the provided underlying type.
+ ///
+ /// The underlying type.
+ /// An instance of this type.
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ public static StringThing From(global::System.String value)
+ {
+ if (value is null)
+ {
+ ThrowHelper.ThrowWhenCreatedWithNull();
+ return default !;
+ }
+
+ return new StringThing(value);
+ }
+
+ ///
+ /// Tries to build an instance from the provided underlying type.
+ /// If a normalization method is provided, it will be called.
+ /// If validation is provided, and it fails, false will be returned.
+ ///
+ /// The underlying type.
+ /// An instance of the value object.
+ /// True if the value object can be built, otherwise false.
+ public static bool TryFrom(
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
+#endif
+ global::System.String value,
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)]
+#endif
+ out StringThing vo)
+ {
+ if (value is null)
+ {
+ vo = default;
+ return false;
+ }
+
+ vo = new StringThing(value);
+ return true;
+ }
+
+ ///
+ /// Tries to build an instance from the provided underlying value.
+ /// If a normalization method is provided, it will be called.
+ /// If validation is provided, and it fails, an error will be returned.
+ ///
+ /// The primitive value.
+ /// A containing either the value object, or an error.
+ public static global::Vogen.ValueObjectOrError TryFrom(global::System.String value)
+ {
+ if (value is null)
+ {
+ return new global::Vogen.ValueObjectOrError(global::Vogen.Validation.Invalid("The value provided was null"));
+ }
+
+ return new global::Vogen.ValueObjectOrError(new StringThing(value));
+ }
+
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+#if VOGEN_NO_VALIDATION
+#pragma warning disable CS8775
+ public bool IsInitialized() => true;
+#pragma warning restore CS8775
+#else
+ public bool IsInitialized() => _isInitialized;
+#endif
+#nullable disable
+ public static class Comparers
+ {
+ private class __StringEqualityComparer : global::System.Collections.Generic.IEqualityComparer
+ {
+ readonly global::System.StringComparer _comparer;
+ public __StringEqualityComparer(global::System.StringComparer comparer)
+ {
+ _comparer = comparer;
+ }
+
+ public bool Equals(StringThing x, StringThing y)
+ {
+ return _comparer.Equals(x._value, y._value);
+ }
+
+ public int GetHashCode(StringThing obj)
+ {
+ return _comparer.GetHashCode(obj._value);
+ }
+ }
+
+ public static global::System.Collections.Generic.IEqualityComparer Ordinal => new __StringEqualityComparer(global::System.StringComparer.Ordinal);
+ public static global::System.Collections.Generic.IEqualityComparer OrdinalIgnoreCase => new __StringEqualityComparer(global::System.StringComparer.OrdinalIgnoreCase);
+ public static global::System.Collections.Generic.IEqualityComparer CurrentCulture => new __StringEqualityComparer(global::System.StringComparer.CurrentCulture);
+ public static global::System.Collections.Generic.IEqualityComparer CurrentCultureIgnoreCase => new __StringEqualityComparer(global::System.StringComparer.CurrentCultureIgnoreCase);
+ public static global::System.Collections.Generic.IEqualityComparer InvariantCulture => new __StringEqualityComparer(global::System.StringComparer.InvariantCulture);
+ public static global::System.Collections.Generic.IEqualityComparer InvariantCultureIgnoreCase => new __StringEqualityComparer(global::System.StringComparer.InvariantCultureIgnoreCase);
+#nullable restore
+ }
+
+ // only called internally when something has been deserialized into
+ // its primitive type.
+ private static StringThing __Deserialize(global::System.String value)
+ {
+ if (value is null)
+ {
+ ThrowHelper.ThrowWhenCreatedWithNull();
+ return default !;
+ }
+
+ return new StringThing(value);
+ }
+
+ public global::System.Boolean Equals(StringThing other)
+ {
+ if (ReferenceEquals(null, other))
+ {
+ return false;
+ }
+
+ // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals.
+ // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type.
+ if (!IsInitialized() || !other.IsInitialized())
+ return false;
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+
+ return GetType() == other.GetType() && global::System.StringComparer.OrdinalIgnoreCase.Equals(Value, other.Value);
+ }
+
+ public global::System.Boolean Equals(StringThing other, global::System.Collections.Generic.IEqualityComparer comparer)
+ {
+ return comparer.Equals(this, other);
+ }
+
+ public global::System.Boolean Equals(global::System.String primitive)
+ {
+ return global::System.StringComparer.OrdinalIgnoreCase.Equals(Value, primitive);
+ }
+
+ public global::System.Boolean Equals(global::System.String primitive, global::System.StringComparer comparer)
+ {
+ return comparer.Equals(Value, primitive);
+ }
+
+ public override global::System.Boolean Equals(global::System.Object obj)
+ {
+ return Equals(obj as StringThing);
+ }
+
+ public static global::System.Boolean operator ==(StringThing left, StringThing right) => Equals(left, right);
+ public static global::System.Boolean operator !=(StringThing left, StringThing right) => !Equals(left, right);
+ public static global::System.Boolean operator ==(StringThing left, global::System.String right) => global::System.StringComparer.OrdinalIgnoreCase.Equals(left.Value, right);
+ public static global::System.Boolean operator ==(global::System.String left, StringThing right) => global::System.StringComparer.OrdinalIgnoreCase.Equals(left, right.Value);
+ public static global::System.Boolean operator !=(global::System.String left, StringThing right) => !(left == right);
+ public static global::System.Boolean operator !=(StringThing left, global::System.String right) => !(left == right);
+ public static explicit operator StringThing(global::System.String value) => From(value);
+ public static explicit operator global::System.String(StringThing value) => value.Value;
+ public int CompareTo(StringThing other)
+ {
+ if (other is null)
+ return 1;
+ return Value.CompareTo(other.Value);
+ }
+
+ public int CompareTo(object other)
+ {
+ if (other is null)
+ return 1;
+ if (other is StringThing x)
+ return CompareTo(x);
+ ThrowHelper.ThrowArgumentException("Cannot compare to object as it is not of type StringThing", nameof(other));
+ return 0;
+ }
+
+ ///
+ ///
+ ///
+ /// True if the value passes any validation (after running any optional normalization).
+ ///
+ public static global::System.Boolean TryParse(
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
+#endif
+ global::System.String s, global::System.IFormatProvider provider,
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)]
+#endif
+ out StringThing result)
+ {
+ if (s is null)
+ {
+ result = default;
+ return false;
+ }
+
+ result = new StringThing(s);
+ return true;
+ }
+
+ ///
+ ///
+ ///
+ /// The value created via the method.
+ ///
+ /// Thrown when the value can be parsed, but is not valid.
+ public static StringThing Parse(global::System.String s, global::System.IFormatProvider provider)
+ {
+ return From(s);
+ }
+
+#nullable disable
+#nullable restore
+#nullable disable
+ ///
+ public System.TypeCode GetTypeCode()
+ {
+ return IsInitialized() ? Value.GetTypeCode() : default;
+ }
+
+ ///
+ bool System.IConvertible.ToBoolean(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToBoolean(provider) : default;
+ }
+
+ ///
+ char System.IConvertible.ToChar(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToChar(provider) : default;
+ }
+
+ ///
+ sbyte System.IConvertible.ToSByte(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToSByte(provider) : default;
+ }
+
+ ///
+ byte System.IConvertible.ToByte(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToByte(provider) : default;
+ }
+
+ ///
+ short System.IConvertible.ToInt16(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt16(provider) : default;
+ }
+
+ ///
+ ushort System.IConvertible.ToUInt16(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt16(provider) : default;
+ }
+
+ ///
+ int System.IConvertible.ToInt32(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt32(provider) : default;
+ }
+
+ ///
+ uint System.IConvertible.ToUInt32(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt32(provider) : default;
+ }
+
+ ///
+ long System.IConvertible.ToInt64(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt64(provider) : default;
+ }
+
+ ///
+ ulong System.IConvertible.ToUInt64(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt64(provider) : default;
+ }
+
+ ///
+ float System.IConvertible.ToSingle(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToSingle(provider) : default;
+ }
+
+ ///
+ double System.IConvertible.ToDouble(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDouble(provider) : default;
+ }
+
+ ///
+ decimal System.IConvertible.ToDecimal(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDecimal(provider) : default;
+ }
+
+ ///
+ System.DateTime System.IConvertible.ToDateTime(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDateTime(provider) : default;
+ }
+
+ ///
+ object System.IConvertible.ToType(global::System.Type @type, global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToType(@type, provider) : default;
+ }
+
+#nullable restore
+ public override global::System.Int32 GetHashCode()
+ {
+ unchecked // Overflow is fine, just wrap
+ {
+ global::System.Int32 hash = (global::System.Int32)2166136261;
+ hash = (hash * 16777619) ^ GetType().GetHashCode();
+ hash = (hash * 16777619) ^ global::System.StringComparer.OrdinalIgnoreCase.GetHashCode(Value);
+ return hash;
+ }
+ }
+
+ ///
+ public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]";
+ ///
+ public global::System.String ToString(global::System.IFormatProvider provider) => IsInitialized() ? Value.ToString(provider) ?? "" : "[UNINITIALIZED]";
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ private void EnsureInitialized()
+ {
+ if (!IsInitialized())
+ {
+#if DEBUG
+ ThrowHelper.ThrowWhenNotInitialized(_stackTrace);
+#else
+ ThrowHelper.ThrowWhenNotInitialized();
+#endif
+ }
+ }
+
+#nullable disable
+ ///
+ /// Converts a StringThing to or from JSON.
+ ///
+ public partial class StringThingSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter
+ {
+ public override StringThing Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ return DeserializeJson(reader.GetString());
+ }
+
+ public override void Write(global::System.Text.Json.Utf8JsonWriter writer, StringThing value, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ writer.WriteStringValue(value.Value);
+ }
+
+#if NET6_0_OR_GREATER
+ public override StringThing ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ return DeserializeJson(reader.GetString());
+ }
+
+ public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, StringThing value, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ writer.WritePropertyName(value.Value);
+ }
+#endif
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation)
+ {
+ var e = ThrowHelper.CreateValidationException(validation);
+ throw new global::System.Text.Json.JsonException(null, e);
+ }
+
+ private static void ThrowJsonExceptionWhenNull(global::System.String value)
+ {
+ if (value == null)
+ {
+ var e = ThrowHelper.CreateCannotBeNullException();
+ throw new global::System.Text.Json.JsonException(null, e);
+ }
+ }
+
+ private static StringThing DeserializeJson(global::System.String value)
+ {
+ ThrowJsonExceptionWhenNull(value);
+ return new StringThing(value);
+ }
+ }
+
+#nullable restore
+#nullable disable
+ class StringThingTypeConverter : global::System.ComponentModel.TypeConverter
+ {
+ public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType)
+ {
+ return sourceType == typeof(global::System.String) || base.CanConvertFrom(context, sourceType);
+ }
+
+ public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value)
+ {
+ var stringValue = value as global::System.String;
+ if (stringValue != null)
+ {
+ return StringThing.__Deserialize(stringValue);
+ }
+
+ return base.ConvertFrom(context, culture, value);
+ }
+
+ public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType)
+ {
+ return sourceType == typeof(global::System.String) || base.CanConvertTo(context, sourceType);
+ }
+
+ public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType)
+ {
+ if (value is StringThing idValue)
+ {
+ if (destinationType == typeof(global::System.String))
+ {
+ return idValue.Value;
+ }
+ }
+
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+ }
+
+#nullable restore
+ internal sealed class StringThingDebugView
+ {
+ private readonly StringThing _t;
+ StringThingDebugView(StringThing t)
+ {
+ _t = t;
+ }
+
+ public global::System.String UnderlyingType => "global::System.String";
+ public global::System.String Value => _t.Value;
+ public global::System.String Conversions => @"[global::System.Text.Json.Serialization.JsonConverter(typeof(StringThingSystemTextJsonConverter))]
+[global::System.ComponentModel.TypeConverter(typeof(StringThingTypeConverter))]
+";
+ }
+
+ static class ThrowHelper
+ {
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message);
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg);
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException();
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object.");
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? "");
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenValidationFails(global::Vogen.Validation validation)
+ {
+ throw CreateValidationException(validation);
+ }
+
+ internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message);
+ internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null.");
+ internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation)
+ {
+ var ex = CreateValidationException(validation.ErrorMessage);
+ if (validation.Data != null)
+ {
+ foreach (var kvp in validation.Data)
+ {
+ ex.Data[kvp.Key] = kvp.Value;
+ }
+ }
+
+ return ex;
+ }
+ }
+ }
+}
+]
\ No newline at end of file
diff --git a/tests/SnapshotTests/StringComparison/snapshots/snap-v4.8/StringComparisonGenerationTests.Generates_when_specified.verified.txt b/tests/SnapshotTests/StringComparison/snapshots/snap-v4.8/StringComparisonGenerationTests.Generates_when_specified.verified.txt
index 7c9943c0d1a..5662b7abb80 100644
--- a/tests/SnapshotTests/StringComparison/snapshots/snap-v4.8/StringComparisonGenerationTests.Generates_when_specified.verified.txt
+++ b/tests/SnapshotTests/StringComparison/snapshots/snap-v4.8/StringComparisonGenerationTests.Generates_when_specified.verified.txt
@@ -208,7 +208,7 @@ private readonly global::System.Diagnostics.StackTrace _stackTrace = null!;
public int GetHashCode(StringThing obj)
{
- return _comparer.GetHashCode();
+ return _comparer.GetHashCode(obj._value);
}
}
diff --git a/tests/SnapshotTests/StringComparison/snapshots/snap-v4.8/StringComparisonGenerationTests.Generates_with_default_comparison_OrdinalIgnoreCase.verified.txt b/tests/SnapshotTests/StringComparison/snapshots/snap-v4.8/StringComparisonGenerationTests.Generates_with_default_comparison_OrdinalIgnoreCase.verified.txt
new file mode 100644
index 00000000000..661b6febf13
--- /dev/null
+++ b/tests/SnapshotTests/StringComparison/snapshots/snap-v4.8/StringComparisonGenerationTests.Generates_with_default_comparison_OrdinalIgnoreCase.verified.txt
@@ -0,0 +1,589 @@
+[
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen)
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+// Suppress warnings for 'Override methods on comparable types'.
+#pragma warning disable CA1036
+// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators
+#pragma warning disable MA0097
+// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.'
+// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations.
+#pragma warning disable CS8669, CS8632
+#pragma warning disable CS8604 // Possible null reference argument.
+
+// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member'
+#pragma warning disable CS1591
+// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
+#pragma warning disable CS8767
+namespace generator
+{
+ [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")]
+ public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory
+ {
+ public VogenTypesFactory()
+ {
+ }
+
+ private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary>
+ {
+ {
+ typeof(global::Whatever.StringThing),
+ new global::System.Lazy(() => new global::Whatever.StringThing.StringThingSystemTextJsonConverter())
+ }
+ };
+ public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert);
+ public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value;
+ }
+}
+
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen)
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+// Suppress warnings for 'Override methods on comparable types'.
+#pragma warning disable CA1036
+// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators
+#pragma warning disable MA0097
+// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.'
+// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations.
+#pragma warning disable CS8669, CS8632
+#pragma warning disable CS8604 // Possible null reference argument.
+
+// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member'
+#pragma warning disable CS1591
+// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
+#pragma warning disable CS8767
+namespace Whatever
+{
+ [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")]
+ [global::System.Text.Json.Serialization.JsonConverter(typeof(StringThingSystemTextJsonConverter))]
+ [global::System.ComponentModel.TypeConverter(typeof(StringThingTypeConverter))]
+ [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(StringThingDebugView))]
+ [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.String, Value = { _value }")]
+ public partial class StringThing : global::System.IEquatable, global::System.IEquatable, global::System.IComparable, global::System.IComparable, global::System.IConvertible
+ {
+#if DEBUG
+private readonly global::System.Diagnostics.StackTrace _stackTrace = null!;
+#endif
+#if !VOGEN_NO_VALIDATION
+ private readonly global::System.Boolean _isInitialized;
+#endif
+ private readonly global::System.String _value;
+ ///
+ /// Gets the underlying value if set, otherwise a is thrown.
+ ///
+ public global::System.String Value
+ {
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ EnsureInitialized();
+ return _value;
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ public StringThing()
+ {
+#if DEBUG
+ _stackTrace = new global::System.Diagnostics.StackTrace();
+#endif
+#if !VOGEN_NO_VALIDATION
+ _isInitialized = false;
+#endif
+ _value = default;
+ }
+
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ private StringThing(global::System.String value)
+ {
+ _value = value;
+#if !VOGEN_NO_VALIDATION
+ _isInitialized = true;
+#endif
+ }
+
+ ///
+ /// Builds an instance from the provided underlying type.
+ ///
+ /// The underlying type.
+ /// An instance of this type.
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ public static StringThing From(global::System.String value)
+ {
+ if (value is null)
+ {
+ ThrowHelper.ThrowWhenCreatedWithNull();
+ return default !;
+ }
+
+ return new StringThing(value);
+ }
+
+ ///
+ /// Tries to build an instance from the provided underlying type.
+ /// If a normalization method is provided, it will be called.
+ /// If validation is provided, and it fails, false will be returned.
+ ///
+ /// The underlying type.
+ /// An instance of the value object.
+ /// True if the value object can be built, otherwise false.
+ public static bool TryFrom(
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
+#endif
+ global::System.String value,
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)]
+#endif
+ out StringThing vo)
+ {
+ if (value is null)
+ {
+ vo = default;
+ return false;
+ }
+
+ vo = new StringThing(value);
+ return true;
+ }
+
+ ///
+ /// Tries to build an instance from the provided underlying value.
+ /// If a normalization method is provided, it will be called.
+ /// If validation is provided, and it fails, an error will be returned.
+ ///
+ /// The primitive value.
+ /// A containing either the value object, or an error.
+ public static global::Vogen.ValueObjectOrError TryFrom(global::System.String value)
+ {
+ if (value is null)
+ {
+ return new global::Vogen.ValueObjectOrError(global::Vogen.Validation.Invalid("The value provided was null"));
+ }
+
+ return new global::Vogen.ValueObjectOrError(new StringThing(value));
+ }
+
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+#if VOGEN_NO_VALIDATION
+#pragma warning disable CS8775
+ public bool IsInitialized() => true;
+#pragma warning restore CS8775
+#else
+ public bool IsInitialized() => _isInitialized;
+#endif
+ // only called internally when something has been deserialized into
+ // its primitive type.
+ private static StringThing __Deserialize(global::System.String value)
+ {
+ if (value is null)
+ {
+ ThrowHelper.ThrowWhenCreatedWithNull();
+ return default !;
+ }
+
+ return new StringThing(value);
+ }
+
+ public global::System.Boolean Equals(StringThing other)
+ {
+ if (ReferenceEquals(null, other))
+ {
+ return false;
+ }
+
+ // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals.
+ // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type.
+ if (!IsInitialized() || !other.IsInitialized())
+ return false;
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+
+ return GetType() == other.GetType() && global::System.StringComparer.OrdinalIgnoreCase.Equals(Value, other.Value);
+ }
+
+ public global::System.Boolean Equals(StringThing other, global::System.Collections.Generic.IEqualityComparer comparer)
+ {
+ return comparer.Equals(this, other);
+ }
+
+ public global::System.Boolean Equals(global::System.String primitive)
+ {
+ return global::System.StringComparer.OrdinalIgnoreCase.Equals(Value, primitive);
+ }
+
+ public global::System.Boolean Equals(global::System.String primitive, global::System.StringComparer comparer)
+ {
+ return comparer.Equals(Value, primitive);
+ }
+
+ public override global::System.Boolean Equals(global::System.Object obj)
+ {
+ return Equals(obj as StringThing);
+ }
+
+ public static global::System.Boolean operator ==(StringThing left, StringThing right) => Equals(left, right);
+ public static global::System.Boolean operator !=(StringThing left, StringThing right) => !Equals(left, right);
+ public static global::System.Boolean operator ==(StringThing left, global::System.String right) => global::System.StringComparer.OrdinalIgnoreCase.Equals(left.Value, right);
+ public static global::System.Boolean operator ==(global::System.String left, StringThing right) => global::System.StringComparer.OrdinalIgnoreCase.Equals(left, right.Value);
+ public static global::System.Boolean operator !=(global::System.String left, StringThing right) => !(left == right);
+ public static global::System.Boolean operator !=(StringThing left, global::System.String right) => !(left == right);
+ public static explicit operator StringThing(global::System.String value) => From(value);
+ public static explicit operator global::System.String(StringThing value) => value.Value;
+ public int CompareTo(StringThing other)
+ {
+ if (other is null)
+ return 1;
+ return Value.CompareTo(other.Value);
+ }
+
+ public int CompareTo(object other)
+ {
+ if (other is null)
+ return 1;
+ if (other is StringThing x)
+ return CompareTo(x);
+ ThrowHelper.ThrowArgumentException("Cannot compare to object as it is not of type StringThing", nameof(other));
+ return 0;
+ }
+
+ ///
+ ///
+ ///
+ /// True if the value passes any validation (after running any optional normalization).
+ ///
+ public static global::System.Boolean TryParse(
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
+#endif
+ global::System.String s, global::System.IFormatProvider provider,
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)]
+#endif
+ out StringThing result)
+ {
+ if (s is null)
+ {
+ result = default;
+ return false;
+ }
+
+ result = new StringThing(s);
+ return true;
+ }
+
+ ///
+ ///
+ ///
+ /// The value created via the method.
+ ///
+ /// Thrown when the value can be parsed, but is not valid.
+ public static StringThing Parse(global::System.String s, global::System.IFormatProvider provider)
+ {
+ return From(s);
+ }
+
+#nullable disable
+#nullable restore
+#nullable disable
+ ///
+ public System.TypeCode GetTypeCode()
+ {
+ return IsInitialized() ? Value.GetTypeCode() : default;
+ }
+
+ ///
+ bool System.IConvertible.ToBoolean(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToBoolean(provider) : default;
+ }
+
+ ///
+ char System.IConvertible.ToChar(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToChar(provider) : default;
+ }
+
+ ///
+ sbyte System.IConvertible.ToSByte(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToSByte(provider) : default;
+ }
+
+ ///
+ byte System.IConvertible.ToByte(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToByte(provider) : default;
+ }
+
+ ///
+ short System.IConvertible.ToInt16(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt16(provider) : default;
+ }
+
+ ///
+ ushort System.IConvertible.ToUInt16(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt16(provider) : default;
+ }
+
+ ///
+ int System.IConvertible.ToInt32(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt32(provider) : default;
+ }
+
+ ///
+ uint System.IConvertible.ToUInt32(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt32(provider) : default;
+ }
+
+ ///
+ long System.IConvertible.ToInt64(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt64(provider) : default;
+ }
+
+ ///
+ ulong System.IConvertible.ToUInt64(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt64(provider) : default;
+ }
+
+ ///
+ float System.IConvertible.ToSingle(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToSingle(provider) : default;
+ }
+
+ ///
+ double System.IConvertible.ToDouble(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDouble(provider) : default;
+ }
+
+ ///
+ decimal System.IConvertible.ToDecimal(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDecimal(provider) : default;
+ }
+
+ ///
+ System.DateTime System.IConvertible.ToDateTime(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDateTime(provider) : default;
+ }
+
+ ///
+ object System.IConvertible.ToType(global::System.Type @type, global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToType(@type, provider) : default;
+ }
+
+#nullable restore
+ public override global::System.Int32 GetHashCode()
+ {
+ unchecked // Overflow is fine, just wrap
+ {
+ global::System.Int32 hash = (global::System.Int32)2166136261;
+ hash = (hash * 16777619) ^ GetType().GetHashCode();
+ hash = (hash * 16777619) ^ global::System.StringComparer.OrdinalIgnoreCase.GetHashCode(Value);
+ return hash;
+ }
+ }
+
+ ///
+ public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]";
+ ///
+ public global::System.String ToString(global::System.IFormatProvider provider) => IsInitialized() ? Value.ToString(provider) ?? "" : "[UNINITIALIZED]";
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ private void EnsureInitialized()
+ {
+ if (!IsInitialized())
+ {
+#if DEBUG
+ ThrowHelper.ThrowWhenNotInitialized(_stackTrace);
+#else
+ ThrowHelper.ThrowWhenNotInitialized();
+#endif
+ }
+ }
+
+#nullable disable
+ ///
+ /// Converts a StringThing to or from JSON.
+ ///
+ public partial class StringThingSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter
+ {
+ public override StringThing Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ return DeserializeJson(reader.GetString());
+ }
+
+ public override void Write(global::System.Text.Json.Utf8JsonWriter writer, StringThing value, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ writer.WriteStringValue(value.Value);
+ }
+
+#if NET6_0_OR_GREATER
+ public override StringThing ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ return DeserializeJson(reader.GetString());
+ }
+
+ public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, StringThing value, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ writer.WritePropertyName(value.Value);
+ }
+#endif
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation)
+ {
+ var e = ThrowHelper.CreateValidationException(validation);
+ throw new global::System.Text.Json.JsonException(null, e);
+ }
+
+ private static void ThrowJsonExceptionWhenNull(global::System.String value)
+ {
+ if (value == null)
+ {
+ var e = ThrowHelper.CreateCannotBeNullException();
+ throw new global::System.Text.Json.JsonException(null, e);
+ }
+ }
+
+ private static StringThing DeserializeJson(global::System.String value)
+ {
+ ThrowJsonExceptionWhenNull(value);
+ return new StringThing(value);
+ }
+ }
+
+#nullable restore
+#nullable disable
+ class StringThingTypeConverter : global::System.ComponentModel.TypeConverter
+ {
+ public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType)
+ {
+ return sourceType == typeof(global::System.String) || base.CanConvertFrom(context, sourceType);
+ }
+
+ public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value)
+ {
+ var stringValue = value as global::System.String;
+ if (stringValue != null)
+ {
+ return StringThing.__Deserialize(stringValue);
+ }
+
+ return base.ConvertFrom(context, culture, value);
+ }
+
+ public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType)
+ {
+ return sourceType == typeof(global::System.String) || base.CanConvertTo(context, sourceType);
+ }
+
+ public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType)
+ {
+ if (value is StringThing idValue)
+ {
+ if (destinationType == typeof(global::System.String))
+ {
+ return idValue.Value;
+ }
+ }
+
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+ }
+
+#nullable restore
+ internal sealed class StringThingDebugView
+ {
+ private readonly StringThing _t;
+ StringThingDebugView(StringThing t)
+ {
+ _t = t;
+ }
+
+ public global::System.String UnderlyingType => "global::System.String";
+ public global::System.String Value => _t.Value;
+ public global::System.String Conversions => @"[global::System.Text.Json.Serialization.JsonConverter(typeof(StringThingSystemTextJsonConverter))]
+[global::System.ComponentModel.TypeConverter(typeof(StringThingTypeConverter))]
+";
+ }
+
+ static class ThrowHelper
+ {
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message);
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg);
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException();
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object.");
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? "");
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenValidationFails(global::Vogen.Validation validation)
+ {
+ throw CreateValidationException(validation);
+ }
+
+ internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message);
+ internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null.");
+ internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation)
+ {
+ var ex = CreateValidationException(validation.ErrorMessage);
+ if (validation.Data != null)
+ {
+ foreach (var kvp in validation.Data)
+ {
+ ex.Data[kvp.Key] = kvp.Value;
+ }
+ }
+
+ return ex;
+ }
+ }
+ }
+}
+]
\ No newline at end of file
diff --git a/tests/SnapshotTests/StringComparison/snapshots/snap-v8.0/StringComparisonGenerationTests.Generates_comparers_class_with_default_comparison_when_both_are_specified.verified.txt b/tests/SnapshotTests/StringComparison/snapshots/snap-v8.0/StringComparisonGenerationTests.Generates_comparers_class_with_default_comparison_when_both_are_specified.verified.txt
new file mode 100644
index 00000000000..62b4cd9b562
--- /dev/null
+++ b/tests/SnapshotTests/StringComparison/snapshots/snap-v8.0/StringComparisonGenerationTests.Generates_comparers_class_with_default_comparison_when_both_are_specified.verified.txt
@@ -0,0 +1,620 @@
+[
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen)
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+// Suppress warnings for 'Override methods on comparable types'.
+#pragma warning disable CA1036
+// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators
+#pragma warning disable MA0097
+// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.'
+// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations.
+#pragma warning disable CS8669, CS8632
+#pragma warning disable CS8604 // Possible null reference argument.
+
+// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member'
+#pragma warning disable CS1591
+// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
+#pragma warning disable CS8767
+namespace generator
+{
+ [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")]
+ public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory
+ {
+ public VogenTypesFactory()
+ {
+ }
+
+ private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary>
+ {
+ {
+ typeof(global::Whatever.StringThing),
+ new global::System.Lazy(() => new global::Whatever.StringThing.StringThingSystemTextJsonConverter())
+ }
+ };
+ public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert);
+ public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value;
+ }
+}
+
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen)
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+// Suppress warnings for 'Override methods on comparable types'.
+#pragma warning disable CA1036
+// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators
+#pragma warning disable MA0097
+// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.'
+// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations.
+#pragma warning disable CS8669, CS8632
+#pragma warning disable CS8604 // Possible null reference argument.
+
+// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member'
+#pragma warning disable CS1591
+// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
+#pragma warning disable CS8767
+namespace Whatever
+{
+ [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")]
+ [global::System.Text.Json.Serialization.JsonConverter(typeof(StringThingSystemTextJsonConverter))]
+ [global::System.ComponentModel.TypeConverter(typeof(StringThingTypeConverter))]
+ [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(StringThingDebugView))]
+ [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.String, Value = { _value }")]
+ public partial class StringThing : global::System.IEquatable, global::System.IEquatable, global::System.IComparable, global::System.IComparable, global::System.IParsable, global::System.IConvertible
+ {
+#if DEBUG
+private readonly global::System.Diagnostics.StackTrace _stackTrace = null!;
+#endif
+#if !VOGEN_NO_VALIDATION
+ private readonly global::System.Boolean _isInitialized;
+#endif
+ private readonly global::System.String _value;
+ ///
+ /// Gets the underlying value if set, otherwise a is thrown.
+ ///
+ public global::System.String Value
+ {
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ EnsureInitialized();
+ return _value;
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ public StringThing()
+ {
+#if DEBUG
+ _stackTrace = new global::System.Diagnostics.StackTrace();
+#endif
+#if !VOGEN_NO_VALIDATION
+ _isInitialized = false;
+#endif
+ _value = default;
+ }
+
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ private StringThing(global::System.String value)
+ {
+ _value = value;
+#if !VOGEN_NO_VALIDATION
+ _isInitialized = true;
+#endif
+ }
+
+ ///
+ /// Builds an instance from the provided underlying type.
+ ///
+ /// The underlying type.
+ /// An instance of this type.
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ public static StringThing From(global::System.String value)
+ {
+ if (value is null)
+ {
+ ThrowHelper.ThrowWhenCreatedWithNull();
+ return default !;
+ }
+
+ return new StringThing(value);
+ }
+
+ ///
+ /// Tries to build an instance from the provided underlying type.
+ /// If a normalization method is provided, it will be called.
+ /// If validation is provided, and it fails, false will be returned.
+ ///
+ /// The underlying type.
+ /// An instance of the value object.
+ /// True if the value object can be built, otherwise false.
+ public static bool TryFrom(
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
+#endif
+ global::System.String value,
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)]
+#endif
+ out StringThing vo)
+ {
+ if (value is null)
+ {
+ vo = default;
+ return false;
+ }
+
+ vo = new StringThing(value);
+ return true;
+ }
+
+ ///
+ /// Tries to build an instance from the provided underlying value.
+ /// If a normalization method is provided, it will be called.
+ /// If validation is provided, and it fails, an error will be returned.
+ ///
+ /// The primitive value.
+ /// A containing either the value object, or an error.
+ public static global::Vogen.ValueObjectOrError TryFrom(global::System.String value)
+ {
+ if (value is null)
+ {
+ return new global::Vogen.ValueObjectOrError(global::Vogen.Validation.Invalid("The value provided was null"));
+ }
+
+ return new global::Vogen.ValueObjectOrError(new StringThing(value));
+ }
+
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+#if VOGEN_NO_VALIDATION
+#pragma warning disable CS8775
+ public bool IsInitialized() => true;
+#pragma warning restore CS8775
+#else
+ public bool IsInitialized() => _isInitialized;
+#endif
+#nullable disable
+ public static class Comparers
+ {
+ private class __StringEqualityComparer : global::System.Collections.Generic.IEqualityComparer
+ {
+ readonly global::System.StringComparer _comparer;
+ public __StringEqualityComparer(global::System.StringComparer comparer)
+ {
+ _comparer = comparer;
+ }
+
+ public bool Equals(StringThing x, StringThing y)
+ {
+ return _comparer.Equals(x._value, y._value);
+ }
+
+ public int GetHashCode(StringThing obj)
+ {
+ return _comparer.GetHashCode(obj._value);
+ }
+ }
+
+ public static global::System.Collections.Generic.IEqualityComparer Ordinal => new __StringEqualityComparer(global::System.StringComparer.Ordinal);
+ public static global::System.Collections.Generic.IEqualityComparer OrdinalIgnoreCase => new __StringEqualityComparer(global::System.StringComparer.OrdinalIgnoreCase);
+ public static global::System.Collections.Generic.IEqualityComparer CurrentCulture => new __StringEqualityComparer(global::System.StringComparer.CurrentCulture);
+ public static global::System.Collections.Generic.IEqualityComparer CurrentCultureIgnoreCase => new __StringEqualityComparer(global::System.StringComparer.CurrentCultureIgnoreCase);
+ public static global::System.Collections.Generic.IEqualityComparer InvariantCulture => new __StringEqualityComparer(global::System.StringComparer.InvariantCulture);
+ public static global::System.Collections.Generic.IEqualityComparer InvariantCultureIgnoreCase => new __StringEqualityComparer(global::System.StringComparer.InvariantCultureIgnoreCase);
+#nullable restore
+ }
+
+ // only called internally when something has been deserialized into
+ // its primitive type.
+ private static StringThing __Deserialize(global::System.String value)
+ {
+ if (value is null)
+ {
+ ThrowHelper.ThrowWhenCreatedWithNull();
+ return default !;
+ }
+
+ return new StringThing(value);
+ }
+
+ public global::System.Boolean Equals(StringThing other)
+ {
+ if (ReferenceEquals(null, other))
+ {
+ return false;
+ }
+
+ // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals.
+ // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type.
+ if (!IsInitialized() || !other.IsInitialized())
+ return false;
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+
+ return GetType() == other.GetType() && global::System.StringComparer.OrdinalIgnoreCase.Equals(Value, other.Value);
+ }
+
+ public global::System.Boolean Equals(StringThing other, global::System.Collections.Generic.IEqualityComparer comparer)
+ {
+ return comparer.Equals(this, other);
+ }
+
+ public global::System.Boolean Equals(global::System.String primitive)
+ {
+ return global::System.StringComparer.OrdinalIgnoreCase.Equals(Value, primitive);
+ }
+
+ public global::System.Boolean Equals(global::System.String primitive, global::System.StringComparer comparer)
+ {
+ return comparer.Equals(Value, primitive);
+ }
+
+ public override global::System.Boolean Equals(global::System.Object obj)
+ {
+ return Equals(obj as StringThing);
+ }
+
+ public static global::System.Boolean operator ==(StringThing left, StringThing right) => Equals(left, right);
+ public static global::System.Boolean operator !=(StringThing left, StringThing right) => !Equals(left, right);
+ public static global::System.Boolean operator ==(StringThing left, global::System.String right) => global::System.StringComparer.OrdinalIgnoreCase.Equals(left.Value, right);
+ public static global::System.Boolean operator ==(global::System.String left, StringThing right) => global::System.StringComparer.OrdinalIgnoreCase.Equals(left, right.Value);
+ public static global::System.Boolean operator !=(global::System.String left, StringThing right) => !(left == right);
+ public static global::System.Boolean operator !=(StringThing left, global::System.String right) => !(left == right);
+ public static explicit operator StringThing(global::System.String value) => From(value);
+ public static explicit operator global::System.String(StringThing value) => value.Value;
+ public int CompareTo(StringThing other)
+ {
+ if (other is null)
+ return 1;
+ return Value.CompareTo(other.Value);
+ }
+
+ public int CompareTo(object other)
+ {
+ if (other is null)
+ return 1;
+ if (other is StringThing x)
+ return CompareTo(x);
+ ThrowHelper.ThrowArgumentException("Cannot compare to object as it is not of type StringThing", nameof(other));
+ return 0;
+ }
+
+ ///
+ ///
+ ///
+ /// True if the value passes any validation (after running any optional normalization).
+ ///
+ public static global::System.Boolean TryParse(
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
+#endif
+ global::System.String s, global::System.IFormatProvider provider,
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)]
+#endif
+ out StringThing result)
+ {
+ if (s is null)
+ {
+ result = default;
+ return false;
+ }
+
+ result = new StringThing(s);
+ return true;
+ }
+
+ ///
+ ///
+ ///
+ /// The value created via the method.
+ ///
+ /// Thrown when the value can be parsed, but is not valid.
+ public static StringThing Parse(global::System.String s, global::System.IFormatProvider provider)
+ {
+ return From(s);
+ }
+
+#nullable disable
+#nullable restore
+#nullable disable
+ ///
+ public System.TypeCode GetTypeCode()
+ {
+ return IsInitialized() ? Value.GetTypeCode() : default;
+ }
+
+ ///
+ bool System.IConvertible.ToBoolean(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToBoolean(provider) : default;
+ }
+
+ ///
+ byte System.IConvertible.ToByte(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToByte(provider) : default;
+ }
+
+ ///
+ char System.IConvertible.ToChar(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToChar(provider) : default;
+ }
+
+ ///
+ System.DateTime System.IConvertible.ToDateTime(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDateTime(provider) : default;
+ }
+
+ ///
+ decimal System.IConvertible.ToDecimal(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDecimal(provider) : default;
+ }
+
+ ///
+ double System.IConvertible.ToDouble(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDouble(provider) : default;
+ }
+
+ ///
+ short System.IConvertible.ToInt16(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt16(provider) : default;
+ }
+
+ ///
+ int System.IConvertible.ToInt32(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt32(provider) : default;
+ }
+
+ ///
+ long System.IConvertible.ToInt64(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt64(provider) : default;
+ }
+
+ ///
+ sbyte System.IConvertible.ToSByte(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToSByte(provider) : default;
+ }
+
+ ///
+ float System.IConvertible.ToSingle(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToSingle(provider) : default;
+ }
+
+ ///
+ object System.IConvertible.ToType(global::System.Type @type, global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToType(@type, provider) : default;
+ }
+
+ ///
+ ushort System.IConvertible.ToUInt16(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt16(provider) : default;
+ }
+
+ ///
+ uint System.IConvertible.ToUInt32(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt32(provider) : default;
+ }
+
+ ///
+ ulong System.IConvertible.ToUInt64(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt64(provider) : default;
+ }
+
+#nullable restore
+ public override global::System.Int32 GetHashCode()
+ {
+ unchecked // Overflow is fine, just wrap
+ {
+ global::System.Int32 hash = (global::System.Int32)2166136261;
+ hash = (hash * 16777619) ^ GetType().GetHashCode();
+ hash = (hash * 16777619) ^ global::System.StringComparer.OrdinalIgnoreCase.GetHashCode(Value);
+ return hash;
+ }
+ }
+
+ ///
+ public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]";
+ ///
+ public global::System.String ToString(global::System.IFormatProvider provider) => IsInitialized() ? Value.ToString(provider) ?? "" : "[UNINITIALIZED]";
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ private void EnsureInitialized()
+ {
+ if (!IsInitialized())
+ {
+#if DEBUG
+ ThrowHelper.ThrowWhenNotInitialized(_stackTrace);
+#else
+ ThrowHelper.ThrowWhenNotInitialized();
+#endif
+ }
+ }
+
+#nullable disable
+ ///
+ /// Converts a StringThing to or from JSON.
+ ///
+ public partial class StringThingSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter
+ {
+ public override StringThing Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ return DeserializeJson(reader.GetString());
+ }
+
+ public override void Write(global::System.Text.Json.Utf8JsonWriter writer, StringThing value, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ writer.WriteStringValue(value.Value);
+ }
+
+#if NET6_0_OR_GREATER
+ public override StringThing ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ return DeserializeJson(reader.GetString());
+ }
+
+ public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, StringThing value, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ writer.WritePropertyName(value.Value);
+ }
+#endif
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation)
+ {
+ var e = ThrowHelper.CreateValidationException(validation);
+ throw new global::System.Text.Json.JsonException(null, e);
+ }
+
+ private static void ThrowJsonExceptionWhenNull(global::System.String value)
+ {
+ if (value == null)
+ {
+ var e = ThrowHelper.CreateCannotBeNullException();
+ throw new global::System.Text.Json.JsonException(null, e);
+ }
+ }
+
+ private static StringThing DeserializeJson(global::System.String value)
+ {
+ ThrowJsonExceptionWhenNull(value);
+ return new StringThing(value);
+ }
+ }
+
+#nullable restore
+#nullable disable
+ class StringThingTypeConverter : global::System.ComponentModel.TypeConverter
+ {
+ public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType)
+ {
+ return sourceType == typeof(global::System.String) || base.CanConvertFrom(context, sourceType);
+ }
+
+ public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value)
+ {
+ var stringValue = value as global::System.String;
+ if (stringValue != null)
+ {
+ return StringThing.__Deserialize(stringValue);
+ }
+
+ return base.ConvertFrom(context, culture, value);
+ }
+
+ public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType)
+ {
+ return sourceType == typeof(global::System.String) || base.CanConvertTo(context, sourceType);
+ }
+
+ public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType)
+ {
+ if (value is StringThing idValue)
+ {
+ if (destinationType == typeof(global::System.String))
+ {
+ return idValue.Value;
+ }
+ }
+
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+ }
+
+#nullable restore
+ internal sealed class StringThingDebugView
+ {
+ private readonly StringThing _t;
+ StringThingDebugView(StringThing t)
+ {
+ _t = t;
+ }
+
+ public global::System.String UnderlyingType => "global::System.String";
+ public global::System.String Value => _t.Value;
+ public global::System.String Conversions => @"[global::System.Text.Json.Serialization.JsonConverter(typeof(StringThingSystemTextJsonConverter))]
+[global::System.ComponentModel.TypeConverter(typeof(StringThingTypeConverter))]
+";
+ }
+
+ static class ThrowHelper
+ {
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message);
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg);
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException();
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object.");
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? "");
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenValidationFails(global::Vogen.Validation validation)
+ {
+ throw CreateValidationException(validation);
+ }
+
+ internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message);
+ internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null.");
+ internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation)
+ {
+ var ex = CreateValidationException(validation.ErrorMessage);
+ if (validation.Data != null)
+ {
+ foreach (var kvp in validation.Data)
+ {
+ ex.Data[kvp.Key] = kvp.Value;
+ }
+ }
+
+ return ex;
+ }
+ }
+ }
+}
+]
\ No newline at end of file
diff --git a/tests/SnapshotTests/StringComparison/snapshots/snap-v8.0/StringComparisonGenerationTests.Generates_when_specified.verified.txt b/tests/SnapshotTests/StringComparison/snapshots/snap-v8.0/StringComparisonGenerationTests.Generates_when_specified.verified.txt
index 351990388f5..26f2975c54d 100644
--- a/tests/SnapshotTests/StringComparison/snapshots/snap-v8.0/StringComparisonGenerationTests.Generates_when_specified.verified.txt
+++ b/tests/SnapshotTests/StringComparison/snapshots/snap-v8.0/StringComparisonGenerationTests.Generates_when_specified.verified.txt
@@ -208,7 +208,7 @@ private readonly global::System.Diagnostics.StackTrace _stackTrace = null!;
public int GetHashCode(StringThing obj)
{
- return _comparer.GetHashCode();
+ return _comparer.GetHashCode(obj._value);
}
}
diff --git a/tests/SnapshotTests/StringComparison/snapshots/snap-v8.0/StringComparisonGenerationTests.Generates_with_default_comparison_OrdinalIgnoreCase.verified.txt b/tests/SnapshotTests/StringComparison/snapshots/snap-v8.0/StringComparisonGenerationTests.Generates_with_default_comparison_OrdinalIgnoreCase.verified.txt
new file mode 100644
index 00000000000..461a0c4ec01
--- /dev/null
+++ b/tests/SnapshotTests/StringComparison/snapshots/snap-v8.0/StringComparisonGenerationTests.Generates_with_default_comparison_OrdinalIgnoreCase.verified.txt
@@ -0,0 +1,589 @@
+[
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen)
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+// Suppress warnings for 'Override methods on comparable types'.
+#pragma warning disable CA1036
+// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators
+#pragma warning disable MA0097
+// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.'
+// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations.
+#pragma warning disable CS8669, CS8632
+#pragma warning disable CS8604 // Possible null reference argument.
+
+// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member'
+#pragma warning disable CS1591
+// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
+#pragma warning disable CS8767
+namespace generator
+{
+ [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")]
+ public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory
+ {
+ public VogenTypesFactory()
+ {
+ }
+
+ private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary>
+ {
+ {
+ typeof(global::Whatever.StringThing),
+ new global::System.Lazy(() => new global::Whatever.StringThing.StringThingSystemTextJsonConverter())
+ }
+ };
+ public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert);
+ public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value;
+ }
+}
+
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen)
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+// Suppress warnings for 'Override methods on comparable types'.
+#pragma warning disable CA1036
+// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators
+#pragma warning disable MA0097
+// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.'
+// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations.
+#pragma warning disable CS8669, CS8632
+#pragma warning disable CS8604 // Possible null reference argument.
+
+// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member'
+#pragma warning disable CS1591
+// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
+#pragma warning disable CS8767
+namespace Whatever
+{
+ [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")]
+ [global::System.Text.Json.Serialization.JsonConverter(typeof(StringThingSystemTextJsonConverter))]
+ [global::System.ComponentModel.TypeConverter(typeof(StringThingTypeConverter))]
+ [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(StringThingDebugView))]
+ [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.String, Value = { _value }")]
+ public partial class StringThing : global::System.IEquatable, global::System.IEquatable, global::System.IComparable, global::System.IComparable, global::System.IParsable, global::System.IConvertible
+ {
+#if DEBUG
+private readonly global::System.Diagnostics.StackTrace _stackTrace = null!;
+#endif
+#if !VOGEN_NO_VALIDATION
+ private readonly global::System.Boolean _isInitialized;
+#endif
+ private readonly global::System.String _value;
+ ///
+ /// Gets the underlying value if set, otherwise a is thrown.
+ ///
+ public global::System.String Value
+ {
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ EnsureInitialized();
+ return _value;
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ public StringThing()
+ {
+#if DEBUG
+ _stackTrace = new global::System.Diagnostics.StackTrace();
+#endif
+#if !VOGEN_NO_VALIDATION
+ _isInitialized = false;
+#endif
+ _value = default;
+ }
+
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ private StringThing(global::System.String value)
+ {
+ _value = value;
+#if !VOGEN_NO_VALIDATION
+ _isInitialized = true;
+#endif
+ }
+
+ ///
+ /// Builds an instance from the provided underlying type.
+ ///
+ /// The underlying type.
+ /// An instance of this type.
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ public static StringThing From(global::System.String value)
+ {
+ if (value is null)
+ {
+ ThrowHelper.ThrowWhenCreatedWithNull();
+ return default !;
+ }
+
+ return new StringThing(value);
+ }
+
+ ///
+ /// Tries to build an instance from the provided underlying type.
+ /// If a normalization method is provided, it will be called.
+ /// If validation is provided, and it fails, false will be returned.
+ ///
+ /// The underlying type.
+ /// An instance of the value object.
+ /// True if the value object can be built, otherwise false.
+ public static bool TryFrom(
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
+#endif
+ global::System.String value,
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)]
+#endif
+ out StringThing vo)
+ {
+ if (value is null)
+ {
+ vo = default;
+ return false;
+ }
+
+ vo = new StringThing(value);
+ return true;
+ }
+
+ ///
+ /// Tries to build an instance from the provided underlying value.
+ /// If a normalization method is provided, it will be called.
+ /// If validation is provided, and it fails, an error will be returned.
+ ///
+ /// The primitive value.
+ /// A containing either the value object, or an error.
+ public static global::Vogen.ValueObjectOrError TryFrom(global::System.String value)
+ {
+ if (value is null)
+ {
+ return new global::Vogen.ValueObjectOrError(global::Vogen.Validation.Invalid("The value provided was null"));
+ }
+
+ return new global::Vogen.ValueObjectOrError(new StringThing(value));
+ }
+
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+#if VOGEN_NO_VALIDATION
+#pragma warning disable CS8775
+ public bool IsInitialized() => true;
+#pragma warning restore CS8775
+#else
+ public bool IsInitialized() => _isInitialized;
+#endif
+ // only called internally when something has been deserialized into
+ // its primitive type.
+ private static StringThing __Deserialize(global::System.String value)
+ {
+ if (value is null)
+ {
+ ThrowHelper.ThrowWhenCreatedWithNull();
+ return default !;
+ }
+
+ return new StringThing(value);
+ }
+
+ public global::System.Boolean Equals(StringThing other)
+ {
+ if (ReferenceEquals(null, other))
+ {
+ return false;
+ }
+
+ // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals.
+ // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type.
+ if (!IsInitialized() || !other.IsInitialized())
+ return false;
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+
+ return GetType() == other.GetType() && global::System.StringComparer.OrdinalIgnoreCase.Equals(Value, other.Value);
+ }
+
+ public global::System.Boolean Equals(StringThing other, global::System.Collections.Generic.IEqualityComparer comparer)
+ {
+ return comparer.Equals(this, other);
+ }
+
+ public global::System.Boolean Equals(global::System.String primitive)
+ {
+ return global::System.StringComparer.OrdinalIgnoreCase.Equals(Value, primitive);
+ }
+
+ public global::System.Boolean Equals(global::System.String primitive, global::System.StringComparer comparer)
+ {
+ return comparer.Equals(Value, primitive);
+ }
+
+ public override global::System.Boolean Equals(global::System.Object obj)
+ {
+ return Equals(obj as StringThing);
+ }
+
+ public static global::System.Boolean operator ==(StringThing left, StringThing right) => Equals(left, right);
+ public static global::System.Boolean operator !=(StringThing left, StringThing right) => !Equals(left, right);
+ public static global::System.Boolean operator ==(StringThing left, global::System.String right) => global::System.StringComparer.OrdinalIgnoreCase.Equals(left.Value, right);
+ public static global::System.Boolean operator ==(global::System.String left, StringThing right) => global::System.StringComparer.OrdinalIgnoreCase.Equals(left, right.Value);
+ public static global::System.Boolean operator !=(global::System.String left, StringThing right) => !(left == right);
+ public static global::System.Boolean operator !=(StringThing left, global::System.String right) => !(left == right);
+ public static explicit operator StringThing(global::System.String value) => From(value);
+ public static explicit operator global::System.String(StringThing value) => value.Value;
+ public int CompareTo(StringThing other)
+ {
+ if (other is null)
+ return 1;
+ return Value.CompareTo(other.Value);
+ }
+
+ public int CompareTo(object other)
+ {
+ if (other is null)
+ return 1;
+ if (other is StringThing x)
+ return CompareTo(x);
+ ThrowHelper.ThrowArgumentException("Cannot compare to object as it is not of type StringThing", nameof(other));
+ return 0;
+ }
+
+ ///
+ ///
+ ///
+ /// True if the value passes any validation (after running any optional normalization).
+ ///
+ public static global::System.Boolean TryParse(
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
+#endif
+ global::System.String s, global::System.IFormatProvider provider,
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)]
+#endif
+ out StringThing result)
+ {
+ if (s is null)
+ {
+ result = default;
+ return false;
+ }
+
+ result = new StringThing(s);
+ return true;
+ }
+
+ ///
+ ///
+ ///
+ /// The value created via the method.
+ ///
+ /// Thrown when the value can be parsed, but is not valid.
+ public static StringThing Parse(global::System.String s, global::System.IFormatProvider provider)
+ {
+ return From(s);
+ }
+
+#nullable disable
+#nullable restore
+#nullable disable
+ ///
+ public System.TypeCode GetTypeCode()
+ {
+ return IsInitialized() ? Value.GetTypeCode() : default;
+ }
+
+ ///
+ bool System.IConvertible.ToBoolean(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToBoolean(provider) : default;
+ }
+
+ ///
+ byte System.IConvertible.ToByte(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToByte(provider) : default;
+ }
+
+ ///
+ char System.IConvertible.ToChar(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToChar(provider) : default;
+ }
+
+ ///
+ System.DateTime System.IConvertible.ToDateTime(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDateTime(provider) : default;
+ }
+
+ ///
+ decimal System.IConvertible.ToDecimal(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDecimal(provider) : default;
+ }
+
+ ///
+ double System.IConvertible.ToDouble(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDouble(provider) : default;
+ }
+
+ ///
+ short System.IConvertible.ToInt16(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt16(provider) : default;
+ }
+
+ ///
+ int System.IConvertible.ToInt32(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt32(provider) : default;
+ }
+
+ ///
+ long System.IConvertible.ToInt64(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt64(provider) : default;
+ }
+
+ ///
+ sbyte System.IConvertible.ToSByte(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToSByte(provider) : default;
+ }
+
+ ///
+ float System.IConvertible.ToSingle(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToSingle(provider) : default;
+ }
+
+ ///
+ object System.IConvertible.ToType(global::System.Type @type, global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToType(@type, provider) : default;
+ }
+
+ ///
+ ushort System.IConvertible.ToUInt16(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt16(provider) : default;
+ }
+
+ ///
+ uint System.IConvertible.ToUInt32(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt32(provider) : default;
+ }
+
+ ///
+ ulong System.IConvertible.ToUInt64(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt64(provider) : default;
+ }
+
+#nullable restore
+ public override global::System.Int32 GetHashCode()
+ {
+ unchecked // Overflow is fine, just wrap
+ {
+ global::System.Int32 hash = (global::System.Int32)2166136261;
+ hash = (hash * 16777619) ^ GetType().GetHashCode();
+ hash = (hash * 16777619) ^ global::System.StringComparer.OrdinalIgnoreCase.GetHashCode(Value);
+ return hash;
+ }
+ }
+
+ ///
+ public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]";
+ ///
+ public global::System.String ToString(global::System.IFormatProvider provider) => IsInitialized() ? Value.ToString(provider) ?? "" : "[UNINITIALIZED]";
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ private void EnsureInitialized()
+ {
+ if (!IsInitialized())
+ {
+#if DEBUG
+ ThrowHelper.ThrowWhenNotInitialized(_stackTrace);
+#else
+ ThrowHelper.ThrowWhenNotInitialized();
+#endif
+ }
+ }
+
+#nullable disable
+ ///
+ /// Converts a StringThing to or from JSON.
+ ///
+ public partial class StringThingSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter
+ {
+ public override StringThing Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ return DeserializeJson(reader.GetString());
+ }
+
+ public override void Write(global::System.Text.Json.Utf8JsonWriter writer, StringThing value, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ writer.WriteStringValue(value.Value);
+ }
+
+#if NET6_0_OR_GREATER
+ public override StringThing ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ return DeserializeJson(reader.GetString());
+ }
+
+ public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, StringThing value, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ writer.WritePropertyName(value.Value);
+ }
+#endif
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation)
+ {
+ var e = ThrowHelper.CreateValidationException(validation);
+ throw new global::System.Text.Json.JsonException(null, e);
+ }
+
+ private static void ThrowJsonExceptionWhenNull(global::System.String value)
+ {
+ if (value == null)
+ {
+ var e = ThrowHelper.CreateCannotBeNullException();
+ throw new global::System.Text.Json.JsonException(null, e);
+ }
+ }
+
+ private static StringThing DeserializeJson(global::System.String value)
+ {
+ ThrowJsonExceptionWhenNull(value);
+ return new StringThing(value);
+ }
+ }
+
+#nullable restore
+#nullable disable
+ class StringThingTypeConverter : global::System.ComponentModel.TypeConverter
+ {
+ public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType)
+ {
+ return sourceType == typeof(global::System.String) || base.CanConvertFrom(context, sourceType);
+ }
+
+ public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value)
+ {
+ var stringValue = value as global::System.String;
+ if (stringValue != null)
+ {
+ return StringThing.__Deserialize(stringValue);
+ }
+
+ return base.ConvertFrom(context, culture, value);
+ }
+
+ public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType)
+ {
+ return sourceType == typeof(global::System.String) || base.CanConvertTo(context, sourceType);
+ }
+
+ public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType)
+ {
+ if (value is StringThing idValue)
+ {
+ if (destinationType == typeof(global::System.String))
+ {
+ return idValue.Value;
+ }
+ }
+
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+ }
+
+#nullable restore
+ internal sealed class StringThingDebugView
+ {
+ private readonly StringThing _t;
+ StringThingDebugView(StringThing t)
+ {
+ _t = t;
+ }
+
+ public global::System.String UnderlyingType => "global::System.String";
+ public global::System.String Value => _t.Value;
+ public global::System.String Conversions => @"[global::System.Text.Json.Serialization.JsonConverter(typeof(StringThingSystemTextJsonConverter))]
+[global::System.ComponentModel.TypeConverter(typeof(StringThingTypeConverter))]
+";
+ }
+
+ static class ThrowHelper
+ {
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message);
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg);
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException();
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object.");
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? "");
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenValidationFails(global::Vogen.Validation validation)
+ {
+ throw CreateValidationException(validation);
+ }
+
+ internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message);
+ internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null.");
+ internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation)
+ {
+ var ex = CreateValidationException(validation.ErrorMessage);
+ if (validation.Data != null)
+ {
+ foreach (var kvp in validation.Data)
+ {
+ ex.Data[kvp.Key] = kvp.Value;
+ }
+ }
+
+ return ex;
+ }
+ }
+ }
+}
+]
\ No newline at end of file
diff --git a/tests/SnapshotTests/StringComparison/snapshots/snap-v9.0/StringComparisonGenerationTests.Generates_comparers_class_with_default_comparison_when_both_are_specified.verified.txt b/tests/SnapshotTests/StringComparison/snapshots/snap-v9.0/StringComparisonGenerationTests.Generates_comparers_class_with_default_comparison_when_both_are_specified.verified.txt
new file mode 100644
index 00000000000..62b4cd9b562
--- /dev/null
+++ b/tests/SnapshotTests/StringComparison/snapshots/snap-v9.0/StringComparisonGenerationTests.Generates_comparers_class_with_default_comparison_when_both_are_specified.verified.txt
@@ -0,0 +1,620 @@
+[
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen)
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+// Suppress warnings for 'Override methods on comparable types'.
+#pragma warning disable CA1036
+// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators
+#pragma warning disable MA0097
+// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.'
+// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations.
+#pragma warning disable CS8669, CS8632
+#pragma warning disable CS8604 // Possible null reference argument.
+
+// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member'
+#pragma warning disable CS1591
+// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
+#pragma warning disable CS8767
+namespace generator
+{
+ [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")]
+ public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory
+ {
+ public VogenTypesFactory()
+ {
+ }
+
+ private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary>
+ {
+ {
+ typeof(global::Whatever.StringThing),
+ new global::System.Lazy(() => new global::Whatever.StringThing.StringThingSystemTextJsonConverter())
+ }
+ };
+ public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert);
+ public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value;
+ }
+}
+
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen)
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+// Suppress warnings for 'Override methods on comparable types'.
+#pragma warning disable CA1036
+// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators
+#pragma warning disable MA0097
+// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.'
+// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations.
+#pragma warning disable CS8669, CS8632
+#pragma warning disable CS8604 // Possible null reference argument.
+
+// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member'
+#pragma warning disable CS1591
+// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
+#pragma warning disable CS8767
+namespace Whatever
+{
+ [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")]
+ [global::System.Text.Json.Serialization.JsonConverter(typeof(StringThingSystemTextJsonConverter))]
+ [global::System.ComponentModel.TypeConverter(typeof(StringThingTypeConverter))]
+ [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(StringThingDebugView))]
+ [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.String, Value = { _value }")]
+ public partial class StringThing : global::System.IEquatable, global::System.IEquatable, global::System.IComparable, global::System.IComparable, global::System.IParsable, global::System.IConvertible
+ {
+#if DEBUG
+private readonly global::System.Diagnostics.StackTrace _stackTrace = null!;
+#endif
+#if !VOGEN_NO_VALIDATION
+ private readonly global::System.Boolean _isInitialized;
+#endif
+ private readonly global::System.String _value;
+ ///
+ /// Gets the underlying value if set, otherwise a is thrown.
+ ///
+ public global::System.String Value
+ {
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ EnsureInitialized();
+ return _value;
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ public StringThing()
+ {
+#if DEBUG
+ _stackTrace = new global::System.Diagnostics.StackTrace();
+#endif
+#if !VOGEN_NO_VALIDATION
+ _isInitialized = false;
+#endif
+ _value = default;
+ }
+
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ private StringThing(global::System.String value)
+ {
+ _value = value;
+#if !VOGEN_NO_VALIDATION
+ _isInitialized = true;
+#endif
+ }
+
+ ///
+ /// Builds an instance from the provided underlying type.
+ ///
+ /// The underlying type.
+ /// An instance of this type.
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ public static StringThing From(global::System.String value)
+ {
+ if (value is null)
+ {
+ ThrowHelper.ThrowWhenCreatedWithNull();
+ return default !;
+ }
+
+ return new StringThing(value);
+ }
+
+ ///
+ /// Tries to build an instance from the provided underlying type.
+ /// If a normalization method is provided, it will be called.
+ /// If validation is provided, and it fails, false will be returned.
+ ///
+ /// The underlying type.
+ /// An instance of the value object.
+ /// True if the value object can be built, otherwise false.
+ public static bool TryFrom(
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
+#endif
+ global::System.String value,
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)]
+#endif
+ out StringThing vo)
+ {
+ if (value is null)
+ {
+ vo = default;
+ return false;
+ }
+
+ vo = new StringThing(value);
+ return true;
+ }
+
+ ///
+ /// Tries to build an instance from the provided underlying value.
+ /// If a normalization method is provided, it will be called.
+ /// If validation is provided, and it fails, an error will be returned.
+ ///
+ /// The primitive value.
+ /// A containing either the value object, or an error.
+ public static global::Vogen.ValueObjectOrError TryFrom(global::System.String value)
+ {
+ if (value is null)
+ {
+ return new global::Vogen.ValueObjectOrError(global::Vogen.Validation.Invalid("The value provided was null"));
+ }
+
+ return new global::Vogen.ValueObjectOrError(new StringThing(value));
+ }
+
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+#if VOGEN_NO_VALIDATION
+#pragma warning disable CS8775
+ public bool IsInitialized() => true;
+#pragma warning restore CS8775
+#else
+ public bool IsInitialized() => _isInitialized;
+#endif
+#nullable disable
+ public static class Comparers
+ {
+ private class __StringEqualityComparer : global::System.Collections.Generic.IEqualityComparer
+ {
+ readonly global::System.StringComparer _comparer;
+ public __StringEqualityComparer(global::System.StringComparer comparer)
+ {
+ _comparer = comparer;
+ }
+
+ public bool Equals(StringThing x, StringThing y)
+ {
+ return _comparer.Equals(x._value, y._value);
+ }
+
+ public int GetHashCode(StringThing obj)
+ {
+ return _comparer.GetHashCode(obj._value);
+ }
+ }
+
+ public static global::System.Collections.Generic.IEqualityComparer Ordinal => new __StringEqualityComparer(global::System.StringComparer.Ordinal);
+ public static global::System.Collections.Generic.IEqualityComparer OrdinalIgnoreCase => new __StringEqualityComparer(global::System.StringComparer.OrdinalIgnoreCase);
+ public static global::System.Collections.Generic.IEqualityComparer CurrentCulture => new __StringEqualityComparer(global::System.StringComparer.CurrentCulture);
+ public static global::System.Collections.Generic.IEqualityComparer CurrentCultureIgnoreCase => new __StringEqualityComparer(global::System.StringComparer.CurrentCultureIgnoreCase);
+ public static global::System.Collections.Generic.IEqualityComparer InvariantCulture => new __StringEqualityComparer(global::System.StringComparer.InvariantCulture);
+ public static global::System.Collections.Generic.IEqualityComparer InvariantCultureIgnoreCase => new __StringEqualityComparer(global::System.StringComparer.InvariantCultureIgnoreCase);
+#nullable restore
+ }
+
+ // only called internally when something has been deserialized into
+ // its primitive type.
+ private static StringThing __Deserialize(global::System.String value)
+ {
+ if (value is null)
+ {
+ ThrowHelper.ThrowWhenCreatedWithNull();
+ return default !;
+ }
+
+ return new StringThing(value);
+ }
+
+ public global::System.Boolean Equals(StringThing other)
+ {
+ if (ReferenceEquals(null, other))
+ {
+ return false;
+ }
+
+ // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals.
+ // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type.
+ if (!IsInitialized() || !other.IsInitialized())
+ return false;
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+
+ return GetType() == other.GetType() && global::System.StringComparer.OrdinalIgnoreCase.Equals(Value, other.Value);
+ }
+
+ public global::System.Boolean Equals(StringThing other, global::System.Collections.Generic.IEqualityComparer comparer)
+ {
+ return comparer.Equals(this, other);
+ }
+
+ public global::System.Boolean Equals(global::System.String primitive)
+ {
+ return global::System.StringComparer.OrdinalIgnoreCase.Equals(Value, primitive);
+ }
+
+ public global::System.Boolean Equals(global::System.String primitive, global::System.StringComparer comparer)
+ {
+ return comparer.Equals(Value, primitive);
+ }
+
+ public override global::System.Boolean Equals(global::System.Object obj)
+ {
+ return Equals(obj as StringThing);
+ }
+
+ public static global::System.Boolean operator ==(StringThing left, StringThing right) => Equals(left, right);
+ public static global::System.Boolean operator !=(StringThing left, StringThing right) => !Equals(left, right);
+ public static global::System.Boolean operator ==(StringThing left, global::System.String right) => global::System.StringComparer.OrdinalIgnoreCase.Equals(left.Value, right);
+ public static global::System.Boolean operator ==(global::System.String left, StringThing right) => global::System.StringComparer.OrdinalIgnoreCase.Equals(left, right.Value);
+ public static global::System.Boolean operator !=(global::System.String left, StringThing right) => !(left == right);
+ public static global::System.Boolean operator !=(StringThing left, global::System.String right) => !(left == right);
+ public static explicit operator StringThing(global::System.String value) => From(value);
+ public static explicit operator global::System.String(StringThing value) => value.Value;
+ public int CompareTo(StringThing other)
+ {
+ if (other is null)
+ return 1;
+ return Value.CompareTo(other.Value);
+ }
+
+ public int CompareTo(object other)
+ {
+ if (other is null)
+ return 1;
+ if (other is StringThing x)
+ return CompareTo(x);
+ ThrowHelper.ThrowArgumentException("Cannot compare to object as it is not of type StringThing", nameof(other));
+ return 0;
+ }
+
+ ///
+ ///
+ ///
+ /// True if the value passes any validation (after running any optional normalization).
+ ///
+ public static global::System.Boolean TryParse(
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
+#endif
+ global::System.String s, global::System.IFormatProvider provider,
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)]
+#endif
+ out StringThing result)
+ {
+ if (s is null)
+ {
+ result = default;
+ return false;
+ }
+
+ result = new StringThing(s);
+ return true;
+ }
+
+ ///
+ ///
+ ///
+ /// The value created via the method.
+ ///
+ /// Thrown when the value can be parsed, but is not valid.
+ public static StringThing Parse(global::System.String s, global::System.IFormatProvider provider)
+ {
+ return From(s);
+ }
+
+#nullable disable
+#nullable restore
+#nullable disable
+ ///
+ public System.TypeCode GetTypeCode()
+ {
+ return IsInitialized() ? Value.GetTypeCode() : default;
+ }
+
+ ///
+ bool System.IConvertible.ToBoolean(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToBoolean(provider) : default;
+ }
+
+ ///
+ byte System.IConvertible.ToByte(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToByte(provider) : default;
+ }
+
+ ///
+ char System.IConvertible.ToChar(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToChar(provider) : default;
+ }
+
+ ///
+ System.DateTime System.IConvertible.ToDateTime(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDateTime(provider) : default;
+ }
+
+ ///
+ decimal System.IConvertible.ToDecimal(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDecimal(provider) : default;
+ }
+
+ ///
+ double System.IConvertible.ToDouble(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDouble(provider) : default;
+ }
+
+ ///
+ short System.IConvertible.ToInt16(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt16(provider) : default;
+ }
+
+ ///
+ int System.IConvertible.ToInt32(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt32(provider) : default;
+ }
+
+ ///
+ long System.IConvertible.ToInt64(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt64(provider) : default;
+ }
+
+ ///
+ sbyte System.IConvertible.ToSByte(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToSByte(provider) : default;
+ }
+
+ ///
+ float System.IConvertible.ToSingle(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToSingle(provider) : default;
+ }
+
+ ///
+ object System.IConvertible.ToType(global::System.Type @type, global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToType(@type, provider) : default;
+ }
+
+ ///
+ ushort System.IConvertible.ToUInt16(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt16(provider) : default;
+ }
+
+ ///
+ uint System.IConvertible.ToUInt32(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt32(provider) : default;
+ }
+
+ ///
+ ulong System.IConvertible.ToUInt64(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt64(provider) : default;
+ }
+
+#nullable restore
+ public override global::System.Int32 GetHashCode()
+ {
+ unchecked // Overflow is fine, just wrap
+ {
+ global::System.Int32 hash = (global::System.Int32)2166136261;
+ hash = (hash * 16777619) ^ GetType().GetHashCode();
+ hash = (hash * 16777619) ^ global::System.StringComparer.OrdinalIgnoreCase.GetHashCode(Value);
+ return hash;
+ }
+ }
+
+ ///
+ public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]";
+ ///
+ public global::System.String ToString(global::System.IFormatProvider provider) => IsInitialized() ? Value.ToString(provider) ?? "" : "[UNINITIALIZED]";
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ private void EnsureInitialized()
+ {
+ if (!IsInitialized())
+ {
+#if DEBUG
+ ThrowHelper.ThrowWhenNotInitialized(_stackTrace);
+#else
+ ThrowHelper.ThrowWhenNotInitialized();
+#endif
+ }
+ }
+
+#nullable disable
+ ///
+ /// Converts a StringThing to or from JSON.
+ ///
+ public partial class StringThingSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter
+ {
+ public override StringThing Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ return DeserializeJson(reader.GetString());
+ }
+
+ public override void Write(global::System.Text.Json.Utf8JsonWriter writer, StringThing value, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ writer.WriteStringValue(value.Value);
+ }
+
+#if NET6_0_OR_GREATER
+ public override StringThing ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ return DeserializeJson(reader.GetString());
+ }
+
+ public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, StringThing value, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ writer.WritePropertyName(value.Value);
+ }
+#endif
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation)
+ {
+ var e = ThrowHelper.CreateValidationException(validation);
+ throw new global::System.Text.Json.JsonException(null, e);
+ }
+
+ private static void ThrowJsonExceptionWhenNull(global::System.String value)
+ {
+ if (value == null)
+ {
+ var e = ThrowHelper.CreateCannotBeNullException();
+ throw new global::System.Text.Json.JsonException(null, e);
+ }
+ }
+
+ private static StringThing DeserializeJson(global::System.String value)
+ {
+ ThrowJsonExceptionWhenNull(value);
+ return new StringThing(value);
+ }
+ }
+
+#nullable restore
+#nullable disable
+ class StringThingTypeConverter : global::System.ComponentModel.TypeConverter
+ {
+ public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType)
+ {
+ return sourceType == typeof(global::System.String) || base.CanConvertFrom(context, sourceType);
+ }
+
+ public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value)
+ {
+ var stringValue = value as global::System.String;
+ if (stringValue != null)
+ {
+ return StringThing.__Deserialize(stringValue);
+ }
+
+ return base.ConvertFrom(context, culture, value);
+ }
+
+ public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType)
+ {
+ return sourceType == typeof(global::System.String) || base.CanConvertTo(context, sourceType);
+ }
+
+ public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType)
+ {
+ if (value is StringThing idValue)
+ {
+ if (destinationType == typeof(global::System.String))
+ {
+ return idValue.Value;
+ }
+ }
+
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+ }
+
+#nullable restore
+ internal sealed class StringThingDebugView
+ {
+ private readonly StringThing _t;
+ StringThingDebugView(StringThing t)
+ {
+ _t = t;
+ }
+
+ public global::System.String UnderlyingType => "global::System.String";
+ public global::System.String Value => _t.Value;
+ public global::System.String Conversions => @"[global::System.Text.Json.Serialization.JsonConverter(typeof(StringThingSystemTextJsonConverter))]
+[global::System.ComponentModel.TypeConverter(typeof(StringThingTypeConverter))]
+";
+ }
+
+ static class ThrowHelper
+ {
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message);
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg);
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException();
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object.");
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? "");
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenValidationFails(global::Vogen.Validation validation)
+ {
+ throw CreateValidationException(validation);
+ }
+
+ internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message);
+ internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null.");
+ internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation)
+ {
+ var ex = CreateValidationException(validation.ErrorMessage);
+ if (validation.Data != null)
+ {
+ foreach (var kvp in validation.Data)
+ {
+ ex.Data[kvp.Key] = kvp.Value;
+ }
+ }
+
+ return ex;
+ }
+ }
+ }
+}
+]
\ No newline at end of file
diff --git a/tests/SnapshotTests/StringComparison/snapshots/snap-v9.0/StringComparisonGenerationTests.Generates_when_specified.verified.txt b/tests/SnapshotTests/StringComparison/snapshots/snap-v9.0/StringComparisonGenerationTests.Generates_when_specified.verified.txt
index 351990388f5..26f2975c54d 100644
--- a/tests/SnapshotTests/StringComparison/snapshots/snap-v9.0/StringComparisonGenerationTests.Generates_when_specified.verified.txt
+++ b/tests/SnapshotTests/StringComparison/snapshots/snap-v9.0/StringComparisonGenerationTests.Generates_when_specified.verified.txt
@@ -208,7 +208,7 @@ private readonly global::System.Diagnostics.StackTrace _stackTrace = null!;
public int GetHashCode(StringThing obj)
{
- return _comparer.GetHashCode();
+ return _comparer.GetHashCode(obj._value);
}
}
diff --git a/tests/SnapshotTests/StringComparison/snapshots/snap-v9.0/StringComparisonGenerationTests.Generates_with_default_comparison_OrdinalIgnoreCase.verified.txt b/tests/SnapshotTests/StringComparison/snapshots/snap-v9.0/StringComparisonGenerationTests.Generates_with_default_comparison_OrdinalIgnoreCase.verified.txt
new file mode 100644
index 00000000000..461a0c4ec01
--- /dev/null
+++ b/tests/SnapshotTests/StringComparison/snapshots/snap-v9.0/StringComparisonGenerationTests.Generates_with_default_comparison_OrdinalIgnoreCase.verified.txt
@@ -0,0 +1,589 @@
+[
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen)
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+// Suppress warnings for 'Override methods on comparable types'.
+#pragma warning disable CA1036
+// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators
+#pragma warning disable MA0097
+// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.'
+// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations.
+#pragma warning disable CS8669, CS8632
+#pragma warning disable CS8604 // Possible null reference argument.
+
+// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member'
+#pragma warning disable CS1591
+// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
+#pragma warning disable CS8767
+namespace generator
+{
+ [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")]
+ public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory
+ {
+ public VogenTypesFactory()
+ {
+ }
+
+ private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary>
+ {
+ {
+ typeof(global::Whatever.StringThing),
+ new global::System.Lazy(() => new global::Whatever.StringThing.StringThingSystemTextJsonConverter())
+ }
+ };
+ public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert);
+ public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value;
+ }
+}
+
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen)
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+// Suppress warnings for 'Override methods on comparable types'.
+#pragma warning disable CA1036
+// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators
+#pragma warning disable MA0097
+// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.'
+// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations.
+#pragma warning disable CS8669, CS8632
+#pragma warning disable CS8604 // Possible null reference argument.
+
+// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member'
+#pragma warning disable CS1591
+// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
+#pragma warning disable CS8767
+namespace Whatever
+{
+ [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")]
+ [global::System.Text.Json.Serialization.JsonConverter(typeof(StringThingSystemTextJsonConverter))]
+ [global::System.ComponentModel.TypeConverter(typeof(StringThingTypeConverter))]
+ [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(StringThingDebugView))]
+ [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.String, Value = { _value }")]
+ public partial class StringThing : global::System.IEquatable, global::System.IEquatable, global::System.IComparable, global::System.IComparable, global::System.IParsable, global::System.IConvertible
+ {
+#if DEBUG
+private readonly global::System.Diagnostics.StackTrace _stackTrace = null!;
+#endif
+#if !VOGEN_NO_VALIDATION
+ private readonly global::System.Boolean _isInitialized;
+#endif
+ private readonly global::System.String _value;
+ ///
+ /// Gets the underlying value if set, otherwise a is thrown.
+ ///
+ public global::System.String Value
+ {
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ EnsureInitialized();
+ return _value;
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ public StringThing()
+ {
+#if DEBUG
+ _stackTrace = new global::System.Diagnostics.StackTrace();
+#endif
+#if !VOGEN_NO_VALIDATION
+ _isInitialized = false;
+#endif
+ _value = default;
+ }
+
+ [global::System.Diagnostics.DebuggerStepThroughAttribute]
+ private StringThing(global::System.String value)
+ {
+ _value = value;
+#if !VOGEN_NO_VALIDATION
+ _isInitialized = true;
+#endif
+ }
+
+ ///
+ /// Builds an instance from the provided underlying type.
+ ///
+ /// The underlying type.
+ /// An instance of this type.
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ public static StringThing From(global::System.String value)
+ {
+ if (value is null)
+ {
+ ThrowHelper.ThrowWhenCreatedWithNull();
+ return default !;
+ }
+
+ return new StringThing(value);
+ }
+
+ ///
+ /// Tries to build an instance from the provided underlying type.
+ /// If a normalization method is provided, it will be called.
+ /// If validation is provided, and it fails, false will be returned.
+ ///
+ /// The underlying type.
+ /// An instance of the value object.
+ /// True if the value object can be built, otherwise false.
+ public static bool TryFrom(
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
+#endif
+ global::System.String value,
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)]
+#endif
+ out StringThing vo)
+ {
+ if (value is null)
+ {
+ vo = default;
+ return false;
+ }
+
+ vo = new StringThing(value);
+ return true;
+ }
+
+ ///
+ /// Tries to build an instance from the provided underlying value.
+ /// If a normalization method is provided, it will be called.
+ /// If validation is provided, and it fails, an error will be returned.
+ ///
+ /// The primitive value.
+ /// A containing either the value object, or an error.
+ public static global::Vogen.ValueObjectOrError TryFrom(global::System.String value)
+ {
+ if (value is null)
+ {
+ return new global::Vogen.ValueObjectOrError(global::Vogen.Validation.Invalid("The value provided was null"));
+ }
+
+ return new global::Vogen.ValueObjectOrError(new StringThing(value));
+ }
+
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+#if VOGEN_NO_VALIDATION
+#pragma warning disable CS8775
+ public bool IsInitialized() => true;
+#pragma warning restore CS8775
+#else
+ public bool IsInitialized() => _isInitialized;
+#endif
+ // only called internally when something has been deserialized into
+ // its primitive type.
+ private static StringThing __Deserialize(global::System.String value)
+ {
+ if (value is null)
+ {
+ ThrowHelper.ThrowWhenCreatedWithNull();
+ return default !;
+ }
+
+ return new StringThing(value);
+ }
+
+ public global::System.Boolean Equals(StringThing other)
+ {
+ if (ReferenceEquals(null, other))
+ {
+ return false;
+ }
+
+ // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals.
+ // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type.
+ if (!IsInitialized() || !other.IsInitialized())
+ return false;
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+
+ return GetType() == other.GetType() && global::System.StringComparer.OrdinalIgnoreCase.Equals(Value, other.Value);
+ }
+
+ public global::System.Boolean Equals(StringThing other, global::System.Collections.Generic.IEqualityComparer comparer)
+ {
+ return comparer.Equals(this, other);
+ }
+
+ public global::System.Boolean Equals(global::System.String primitive)
+ {
+ return global::System.StringComparer.OrdinalIgnoreCase.Equals(Value, primitive);
+ }
+
+ public global::System.Boolean Equals(global::System.String primitive, global::System.StringComparer comparer)
+ {
+ return comparer.Equals(Value, primitive);
+ }
+
+ public override global::System.Boolean Equals(global::System.Object obj)
+ {
+ return Equals(obj as StringThing);
+ }
+
+ public static global::System.Boolean operator ==(StringThing left, StringThing right) => Equals(left, right);
+ public static global::System.Boolean operator !=(StringThing left, StringThing right) => !Equals(left, right);
+ public static global::System.Boolean operator ==(StringThing left, global::System.String right) => global::System.StringComparer.OrdinalIgnoreCase.Equals(left.Value, right);
+ public static global::System.Boolean operator ==(global::System.String left, StringThing right) => global::System.StringComparer.OrdinalIgnoreCase.Equals(left, right.Value);
+ public static global::System.Boolean operator !=(global::System.String left, StringThing right) => !(left == right);
+ public static global::System.Boolean operator !=(StringThing left, global::System.String right) => !(left == right);
+ public static explicit operator StringThing(global::System.String value) => From(value);
+ public static explicit operator global::System.String(StringThing value) => value.Value;
+ public int CompareTo(StringThing other)
+ {
+ if (other is null)
+ return 1;
+ return Value.CompareTo(other.Value);
+ }
+
+ public int CompareTo(object other)
+ {
+ if (other is null)
+ return 1;
+ if (other is StringThing x)
+ return CompareTo(x);
+ ThrowHelper.ThrowArgumentException("Cannot compare to object as it is not of type StringThing", nameof(other));
+ return 0;
+ }
+
+ ///
+ ///
+ ///
+ /// True if the value passes any validation (after running any optional normalization).
+ ///
+ public static global::System.Boolean TryParse(
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
+#endif
+ global::System.String s, global::System.IFormatProvider provider,
+#if NETCOREAPP3_0_OR_GREATER
+[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)]
+#endif
+ out StringThing result)
+ {
+ if (s is null)
+ {
+ result = default;
+ return false;
+ }
+
+ result = new StringThing(s);
+ return true;
+ }
+
+ ///
+ ///
+ ///
+ /// The value created via the method.
+ ///
+ /// Thrown when the value can be parsed, but is not valid.
+ public static StringThing Parse(global::System.String s, global::System.IFormatProvider provider)
+ {
+ return From(s);
+ }
+
+#nullable disable
+#nullable restore
+#nullable disable
+ ///
+ public System.TypeCode GetTypeCode()
+ {
+ return IsInitialized() ? Value.GetTypeCode() : default;
+ }
+
+ ///
+ bool System.IConvertible.ToBoolean(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToBoolean(provider) : default;
+ }
+
+ ///
+ byte System.IConvertible.ToByte(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToByte(provider) : default;
+ }
+
+ ///
+ char System.IConvertible.ToChar(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToChar(provider) : default;
+ }
+
+ ///
+ System.DateTime System.IConvertible.ToDateTime(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDateTime(provider) : default;
+ }
+
+ ///
+ decimal System.IConvertible.ToDecimal(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDecimal(provider) : default;
+ }
+
+ ///
+ double System.IConvertible.ToDouble(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToDouble(provider) : default;
+ }
+
+ ///
+ short System.IConvertible.ToInt16(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt16(provider) : default;
+ }
+
+ ///
+ int System.IConvertible.ToInt32(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt32(provider) : default;
+ }
+
+ ///
+ long System.IConvertible.ToInt64(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToInt64(provider) : default;
+ }
+
+ ///
+ sbyte System.IConvertible.ToSByte(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToSByte(provider) : default;
+ }
+
+ ///
+ float System.IConvertible.ToSingle(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToSingle(provider) : default;
+ }
+
+ ///
+ object System.IConvertible.ToType(global::System.Type @type, global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToType(@type, provider) : default;
+ }
+
+ ///
+ ushort System.IConvertible.ToUInt16(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt16(provider) : default;
+ }
+
+ ///
+ uint System.IConvertible.ToUInt32(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt32(provider) : default;
+ }
+
+ ///
+ ulong System.IConvertible.ToUInt64(global::System.IFormatProvider provider)
+ {
+ return IsInitialized() ? (Value as System.IConvertible).ToUInt64(provider) : default;
+ }
+
+#nullable restore
+ public override global::System.Int32 GetHashCode()
+ {
+ unchecked // Overflow is fine, just wrap
+ {
+ global::System.Int32 hash = (global::System.Int32)2166136261;
+ hash = (hash * 16777619) ^ GetType().GetHashCode();
+ hash = (hash * 16777619) ^ global::System.StringComparer.OrdinalIgnoreCase.GetHashCode(Value);
+ return hash;
+ }
+ }
+
+ ///
+ public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]";
+ ///
+ public global::System.String ToString(global::System.IFormatProvider provider) => IsInitialized() ? Value.ToString(provider) ?? "" : "[UNINITIALIZED]";
+ [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ private void EnsureInitialized()
+ {
+ if (!IsInitialized())
+ {
+#if DEBUG
+ ThrowHelper.ThrowWhenNotInitialized(_stackTrace);
+#else
+ ThrowHelper.ThrowWhenNotInitialized();
+#endif
+ }
+ }
+
+#nullable disable
+ ///
+ /// Converts a StringThing to or from JSON.
+ ///
+ public partial class StringThingSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter
+ {
+ public override StringThing Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ return DeserializeJson(reader.GetString());
+ }
+
+ public override void Write(global::System.Text.Json.Utf8JsonWriter writer, StringThing value, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ writer.WriteStringValue(value.Value);
+ }
+
+#if NET6_0_OR_GREATER
+ public override StringThing ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ return DeserializeJson(reader.GetString());
+ }
+
+ public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, StringThing value, global::System.Text.Json.JsonSerializerOptions options)
+ {
+ writer.WritePropertyName(value.Value);
+ }
+#endif
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation)
+ {
+ var e = ThrowHelper.CreateValidationException(validation);
+ throw new global::System.Text.Json.JsonException(null, e);
+ }
+
+ private static void ThrowJsonExceptionWhenNull(global::System.String value)
+ {
+ if (value == null)
+ {
+ var e = ThrowHelper.CreateCannotBeNullException();
+ throw new global::System.Text.Json.JsonException(null, e);
+ }
+ }
+
+ private static StringThing DeserializeJson(global::System.String value)
+ {
+ ThrowJsonExceptionWhenNull(value);
+ return new StringThing(value);
+ }
+ }
+
+#nullable restore
+#nullable disable
+ class StringThingTypeConverter : global::System.ComponentModel.TypeConverter
+ {
+ public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType)
+ {
+ return sourceType == typeof(global::System.String) || base.CanConvertFrom(context, sourceType);
+ }
+
+ public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value)
+ {
+ var stringValue = value as global::System.String;
+ if (stringValue != null)
+ {
+ return StringThing.__Deserialize(stringValue);
+ }
+
+ return base.ConvertFrom(context, culture, value);
+ }
+
+ public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType)
+ {
+ return sourceType == typeof(global::System.String) || base.CanConvertTo(context, sourceType);
+ }
+
+ public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType)
+ {
+ if (value is StringThing idValue)
+ {
+ if (destinationType == typeof(global::System.String))
+ {
+ return idValue.Value;
+ }
+ }
+
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+ }
+
+#nullable restore
+ internal sealed class StringThingDebugView
+ {
+ private readonly StringThing _t;
+ StringThingDebugView(StringThing t)
+ {
+ _t = t;
+ }
+
+ public global::System.String UnderlyingType => "global::System.String";
+ public global::System.String Value => _t.Value;
+ public global::System.String Conversions => @"[global::System.Text.Json.Serialization.JsonConverter(typeof(StringThingSystemTextJsonConverter))]
+[global::System.ComponentModel.TypeConverter(typeof(StringThingTypeConverter))]
+";
+ }
+
+ static class ThrowHelper
+ {
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message);
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg);
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException();
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object.");
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? "");
+#if NETCOREAPP3_0_OR_GREATER
+ [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
+#endif
+ internal static void ThrowWhenValidationFails(global::Vogen.Validation validation)
+ {
+ throw CreateValidationException(validation);
+ }
+
+ internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message);
+ internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null.");
+ internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation)
+ {
+ var ex = CreateValidationException(validation.ErrorMessage);
+ if (validation.Data != null)
+ {
+ foreach (var kvp in validation.Data)
+ {
+ ex.Data[kvp.Key] = kvp.Value;
+ }
+ }
+
+ return ex;
+ }
+ }
+ }
+}
+]
\ No newline at end of file
diff --git a/tests/Vogen.Tests/ConfigurationTests/VogenConfigurationTests.cs b/tests/Vogen.Tests/ConfigurationTests/VogenConfigurationTests.cs
index 541c8153692..fabe071abbf 100644
--- a/tests/Vogen.Tests/ConfigurationTests/VogenConfigurationTests.cs
+++ b/tests/Vogen.Tests/ConfigurationTests/VogenConfigurationTests.cs
@@ -74,7 +74,8 @@ private static VogenConfiguration ConfigWithOmitDebugAs(DebuggerAttributeGenerat
OpenApiSchemaCustomizations.Unspecified,
false,
PrimitiveEqualityGeneration.GenerateOperatorsAndMethods,
- NumericsGeneration.Omit);
+ NumericsGeneration.Omit,
+ StringComparisonDefault.Omit);
}
public class Primitive_equality_generation
@@ -150,7 +151,8 @@ private static VogenConfiguration ConfigWithCastingAs(CastOperator toPrimitiveCa
OpenApiSchemaCustomizations: OpenApiSchemaCustomizations.Unspecified,
false,
PrimitiveEqualityGeneration.GenerateOperatorsAndMethods,
- NumericsGeneration.Omit);
+ NumericsGeneration.Omit,
+ StringComparisonDefault.Omit);
}
public class Conversion
@@ -256,7 +258,8 @@ private static VogenConfiguration ConfigWithOmitConversionsAs(Conversions conver
OpenApiSchemaCustomizations.Unspecified,
false,
PrimitiveEqualityGeneration.GenerateOperatorsAndMethods,
- NumericsGeneration.Omit);
+ NumericsGeneration.Omit,
+ StringComparisonDefault.Omit);
}
public class Comparable