/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/Core/Portable/ConstantValue.cs
909 строк
35 KB
Julien Couvreur
Extensions: propagate return-targeted and local function parameter attributes (#80070)
29 авг 2025, 23:32
Не верифицирован
29 авг 2025, 23:32
7a96051
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection.Metadata; using System.Runtime.CompilerServices; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { internal enum ConstantValueTypeDiscriminator : byte { Nothing, Null = Nothing, Bad, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, NInt, NUInt, Char, Boolean, Single, Double, String, Decimal, DateTime, // Note: new values may need handling in CryptographicHashProvider.ComputeSourceHash } internal abstract partial class ConstantValue : IEquatable<ConstantValue?>, IFormattable { public abstract ConstantValueTypeDiscriminator Discriminator { get; } internal abstract SpecialType SpecialType { get; } public virtual string? StringValue { get { throw new InvalidOperationException(); } } internal virtual Rope? RopeValue { get { throw new InvalidOperationException(); } } public virtual bool BooleanValue { get { throw new InvalidOperationException(); } } public virtual sbyte SByteValue { get { throw new InvalidOperationException(); } } public virtual byte ByteValue { get { throw new InvalidOperationException(); } } // If we can get SByteValue, we can automatically get Int16Value, Int32Value, Int64Value. // This is needed when constant values are reinterpreted during constant folding - // for example a Byte value may be read via UIntValue accessor when folding Byte + Uint // // I have decided that default implementation of Int16Value in terms of SByteValue is appropriate here. // Same pattern is used for providing default implementation of Int32Value in terms of Int16Value and so on. // // An alternative solution would be to override Int16Value, Int32Value, Int64Value whenever I override SByteValue // and so on for Int16Value, Int32Value. That could work mildly faster but would result in a lot more code. public virtual short Int16Value { get { return SByteValue; } } public virtual ushort UInt16Value { get { return ByteValue; } } public virtual int Int32Value { get { return Int16Value; } } public virtual uint UInt32Value { get { return UInt16Value; } } public virtual long Int64Value { get { return Int32Value; } } public virtual ulong UInt64Value { get { return UInt32Value; } } public virtual char CharValue { get { throw new InvalidOperationException(); } } public virtual decimal DecimalValue { get { throw new InvalidOperationException(); } } public virtual DateTime DateTimeValue { get { throw new InvalidOperationException(); } } public virtual double DoubleValue { get { throw new InvalidOperationException(); } } public virtual float SingleValue { get { throw new InvalidOperationException(); } } // returns true if value is in its default (zero-inited) form. public virtual bool IsDefaultValue { get { return false; } } public virtual bool IsOne { get { return false; } } // NOTE: We do not have IsNumericZero. // The reason is that integral zeroes are same as default values // and singles, floats and decimals have multiple zero values. // It appears that in all cases so far we considered isDefaultValue, and not about value being // arithmetic zero (especially when definition is ambiguous). public const ConstantValue? NotAvailable = null; public static ConstantValue Bad { get { return ConstantValueBad.Instance; } } public static ConstantValue Null { get { return ConstantValueNull.Instance; } } public static ConstantValue Nothing { get { return Null; } } // Null, Nothing and Unset are all ConstantValueNull. Null and Nothing are equivalent and represent the null and // nothing constants in C# and VB. Unset indicates an uninitialized ConstantValue. public static ConstantValue Unset { get { return ConstantValueNull.Uninitialized; } } public static ConstantValue True { get { return ConstantValueOne.Boolean; } } public static ConstantValue False { get { return ConstantValueDefault.Boolean; } } public static ConstantValue Create(string? value) { if (value == null) { return Null; } return new ConstantValueString(value); } internal static ConstantValue CreateFromRope(Rope value) { RoslynDebug.Assert(value != null); return new ConstantValueString(value); } public static ConstantValue Create(char value) { if (value == default(char)) { return ConstantValueDefault.Char; } else if (value == (char)1) { return ConstantValueOne.Char; } return new ConstantValueI16(value); } public static ConstantValue Create(sbyte value) { if (value == 0) { return ConstantValueDefault.SByte; } else if (value == 1) { return ConstantValueOne.SByte; } return new ConstantValueI8(value); } public static ConstantValue Create(byte value) { if (value == 0) { return ConstantValueDefault.Byte; } else if (value == 1) { return ConstantValueOne.Byte; } return new ConstantValueI8(value); } public static ConstantValue Create(Int16 value) { if (value == 0) { return ConstantValueDefault.Int16; } else if (value == 1) { return ConstantValueOne.Int16; } return new ConstantValueI16(value); } public static ConstantValue Create(UInt16 value) { if (value == 0) { return ConstantValueDefault.UInt16; } else if (value == 1) { return ConstantValueOne.UInt16; } return new ConstantValueI16(value); } public static ConstantValue Create(Int32 value) { if (value == 0) { return ConstantValueDefault.Int32; } else if (value == 1) { return ConstantValueOne.Int32; } return new ConstantValueI32(value); } public static ConstantValue Create(UInt32 value) { if (value == 0) { return ConstantValueDefault.UInt32; } else if (value == 1) { return ConstantValueOne.UInt32; } return new ConstantValueI32(value); } public static ConstantValue Create(Int64 value) { if (value == 0) { return ConstantValueDefault.Int64; } else if (value == 1) { return ConstantValueOne.Int64; } return new ConstantValueI64(value); } public static ConstantValue Create(UInt64 value) { if (value == 0) { return ConstantValueDefault.UInt64; } else if (value == 1) { return ConstantValueOne.UInt64; } return new ConstantValueI64(value); } public static ConstantValue CreateNativeInt(Int32 value) { if (value == 0) { return ConstantValueDefault.NInt; } else if (value == 1) { return ConstantValueOne.NInt; } return new ConstantValueNativeInt(value); } public static ConstantValue CreateNativeUInt(UInt32 value) { if (value == 0) { return ConstantValueDefault.NUInt; } else if (value == 1) { return ConstantValueOne.NUInt; } return new ConstantValueNativeInt(value); } public static ConstantValue Create(bool value) { if (value) { return ConstantValueOne.Boolean; } else { return ConstantValueDefault.Boolean; } } public static ConstantValue Create(float value) { if (BitConverter.DoubleToInt64Bits(value) == 0) { return ConstantValueDefault.Single; } else if (value == 1) { return ConstantValueOne.Single; } return new ConstantValueSingle(value); } public static ConstantValue CreateSingle(double value) { if (BitConverter.DoubleToInt64Bits(value) == 0) { return ConstantValueDefault.Single; } else if (value == 1) { return ConstantValueOne.Single; } return new ConstantValueSingle(value); } public static ConstantValue Create(double value) { if (BitConverter.DoubleToInt64Bits(value) == 0) { return ConstantValueDefault.Double; } else if (value == 1) { return ConstantValueOne.Double; } return new ConstantValueDouble(value); } public static ConstantValue Create(decimal value) { // The significant bits should be preserved even for Zero or One. // The fourth element of the returned array contains the scale factor and sign. int[] decimalBits = System.Decimal.GetBits(value); if (decimalBits[3] == 0) { if (value == 0) { return ConstantValueDefault.Decimal; } else if (value == 1) { return ConstantValueOne.Decimal; } } return new ConstantValueDecimal(value); } public static ConstantValue Create(DateTime value) { if (value == default(DateTime)) { return ConstantValueDefault.DateTime; } return new ConstantValueDateTime(value); } public static ConstantValue Create(object value, SpecialType st) { var discriminator = GetDiscriminator(st); Debug.Assert(discriminator != ConstantValueTypeDiscriminator.Bad); return Create(value, discriminator); } public static ConstantValue? CreateSizeOf(SpecialType st) { int size = st.SizeInBytes(); return (size == 0) ? null : ConstantValue.Create(size); } public static ConstantValue Create(object value, ConstantValueTypeDiscriminator discriminator) { switch (discriminator) { case ConstantValueTypeDiscriminator.Null: return Null; case ConstantValueTypeDiscriminator.SByte: return Create((sbyte)value); case ConstantValueTypeDiscriminator.Byte: return Create((byte)value); case ConstantValueTypeDiscriminator.Int16: return Create((short)value); case ConstantValueTypeDiscriminator.UInt16: return Create((ushort)value); case ConstantValueTypeDiscriminator.Int32: return Create((int)value); case ConstantValueTypeDiscriminator.UInt32: return Create((uint)value); case ConstantValueTypeDiscriminator.Int64: return Create((long)value); case ConstantValueTypeDiscriminator.UInt64: return Create((ulong)value); case ConstantValueTypeDiscriminator.NInt: return CreateNativeInt((int)value); case ConstantValueTypeDiscriminator.NUInt: return CreateNativeUInt((uint)value); case ConstantValueTypeDiscriminator.Char: return Create((char)value); case ConstantValueTypeDiscriminator.Boolean: return Create((bool)value); case ConstantValueTypeDiscriminator.Single: // values for singles may actually have double precision return value is double ? CreateSingle((double)value) : Create((float)value); case ConstantValueTypeDiscriminator.Double: return Create((double)value); case ConstantValueTypeDiscriminator.Decimal: return Create((decimal)value); case ConstantValueTypeDiscriminator.DateTime: return Create((DateTime)value); case ConstantValueTypeDiscriminator.String: return Create((string)value); default: throw new InvalidOperationException(); //Not using ExceptionUtilities.UnexpectedValue() because this failure path is tested. } } public static ConstantValue Default(SpecialType st) { var discriminator = GetDiscriminator(st); Debug.Assert(discriminator != ConstantValueTypeDiscriminator.Bad); return Default(discriminator); } public static ConstantValue Default(ConstantValueTypeDiscriminator discriminator) { switch (discriminator) { case ConstantValueTypeDiscriminator.Bad: return Bad; case ConstantValueTypeDiscriminator.SByte: return ConstantValueDefault.SByte; case ConstantValueTypeDiscriminator.Byte: return ConstantValueDefault.Byte; case ConstantValueTypeDiscriminator.Int16: return ConstantValueDefault.Int16; case ConstantValueTypeDiscriminator.UInt16: return ConstantValueDefault.UInt16; case ConstantValueTypeDiscriminator.Int32: return ConstantValueDefault.Int32; case ConstantValueTypeDiscriminator.UInt32: return ConstantValueDefault.UInt32; case ConstantValueTypeDiscriminator.Int64: return ConstantValueDefault.Int64; case ConstantValueTypeDiscriminator.UInt64: return ConstantValueDefault.UInt64; case ConstantValueTypeDiscriminator.NInt: return ConstantValueDefault.NInt; case ConstantValueTypeDiscriminator.NUInt: return ConstantValueDefault.NUInt; case ConstantValueTypeDiscriminator.Char: return ConstantValueDefault.Char; case ConstantValueTypeDiscriminator.Boolean: return ConstantValueDefault.Boolean; case ConstantValueTypeDiscriminator.Single: return ConstantValueDefault.Single; case ConstantValueTypeDiscriminator.Double: return ConstantValueDefault.Double; case ConstantValueTypeDiscriminator.Decimal: return ConstantValueDefault.Decimal; case ConstantValueTypeDiscriminator.DateTime: return ConstantValueDefault.DateTime; case ConstantValueTypeDiscriminator.Null: case ConstantValueTypeDiscriminator.String: return Null; } throw ExceptionUtilities.UnexpectedValue(discriminator); } internal static ConstantValueTypeDiscriminator GetDiscriminator(SpecialType st) { switch (st) { case SpecialType.System_SByte: return ConstantValueTypeDiscriminator.SByte; case SpecialType.System_Byte: return ConstantValueTypeDiscriminator.Byte; case SpecialType.System_Int16: return ConstantValueTypeDiscriminator.Int16; case SpecialType.System_UInt16: return ConstantValueTypeDiscriminator.UInt16; case SpecialType.System_Int32: return ConstantValueTypeDiscriminator.Int32; case SpecialType.System_UInt32: return ConstantValueTypeDiscriminator.UInt32; case SpecialType.System_Int64: return ConstantValueTypeDiscriminator.Int64; case SpecialType.System_UInt64: return ConstantValueTypeDiscriminator.UInt64; case SpecialType.System_IntPtr: return ConstantValueTypeDiscriminator.NInt; case SpecialType.System_UIntPtr: return ConstantValueTypeDiscriminator.NUInt; case SpecialType.System_Char: return ConstantValueTypeDiscriminator.Char; case SpecialType.System_Boolean: return ConstantValueTypeDiscriminator.Boolean; case SpecialType.System_Single: return ConstantValueTypeDiscriminator.Single; case SpecialType.System_Double: return ConstantValueTypeDiscriminator.Double; case SpecialType.System_Decimal: return ConstantValueTypeDiscriminator.Decimal; case SpecialType.System_DateTime: return ConstantValueTypeDiscriminator.DateTime; case SpecialType.System_String: return ConstantValueTypeDiscriminator.String; } return ConstantValueTypeDiscriminator.Bad; } public string GetPrimitiveTypeName() { return Discriminator switch { ConstantValueTypeDiscriminator.SByte => "sbyte", ConstantValueTypeDiscriminator.Byte => "byte", ConstantValueTypeDiscriminator.Int16 => "short", ConstantValueTypeDiscriminator.UInt16 => "ushort", ConstantValueTypeDiscriminator.Int32 => "int", ConstantValueTypeDiscriminator.NInt => "nint", ConstantValueTypeDiscriminator.UInt32 => "uint", ConstantValueTypeDiscriminator.NUInt => "nuint", ConstantValueTypeDiscriminator.Int64 => "long", ConstantValueTypeDiscriminator.UInt64 => "ulong", ConstantValueTypeDiscriminator.Char => "char", ConstantValueTypeDiscriminator.Boolean => "bool", ConstantValueTypeDiscriminator.Single => "float", ConstantValueTypeDiscriminator.Double => "double", ConstantValueTypeDiscriminator.String => "string", ConstantValueTypeDiscriminator.Decimal => "decimal", ConstantValueTypeDiscriminator.DateTime => "DateTime", ConstantValueTypeDiscriminator.Null or ConstantValueTypeDiscriminator.Bad => throw ExceptionUtilities.UnexpectedValue(Discriminator), _ => throw ExceptionUtilities.UnexpectedValue(Discriminator) }; } private static SpecialType GetSpecialType(ConstantValueTypeDiscriminator discriminator) { switch (discriminator) { case ConstantValueTypeDiscriminator.SByte: return SpecialType.System_SByte; case ConstantValueTypeDiscriminator.Byte: return SpecialType.System_Byte; case ConstantValueTypeDiscriminator.Int16: return SpecialType.System_Int16; case ConstantValueTypeDiscriminator.UInt16: return SpecialType.System_UInt16; case ConstantValueTypeDiscriminator.Int32: return SpecialType.System_Int32; case ConstantValueTypeDiscriminator.UInt32: return SpecialType.System_UInt32; case ConstantValueTypeDiscriminator.Int64: return SpecialType.System_Int64; case ConstantValueTypeDiscriminator.UInt64: return SpecialType.System_UInt64; case ConstantValueTypeDiscriminator.NInt: return SpecialType.System_IntPtr; case ConstantValueTypeDiscriminator.NUInt: return SpecialType.System_UIntPtr; case ConstantValueTypeDiscriminator.Char: return SpecialType.System_Char; case ConstantValueTypeDiscriminator.Boolean: return SpecialType.System_Boolean; case ConstantValueTypeDiscriminator.Single: return SpecialType.System_Single; case ConstantValueTypeDiscriminator.Double: return SpecialType.System_Double; case ConstantValueTypeDiscriminator.Decimal: return SpecialType.System_Decimal; case ConstantValueTypeDiscriminator.DateTime: return SpecialType.System_DateTime; case ConstantValueTypeDiscriminator.String: return SpecialType.System_String; default: return SpecialType.None; } } public object? Value { get { switch (this.Discriminator) { case ConstantValueTypeDiscriminator.Bad: return null; case ConstantValueTypeDiscriminator.Null: return null; case ConstantValueTypeDiscriminator.SByte: return Boxes.Box(SByteValue); case ConstantValueTypeDiscriminator.Byte: return Boxes.Box(ByteValue); case ConstantValueTypeDiscriminator.Int16: return Boxes.Box(Int16Value); case ConstantValueTypeDiscriminator.UInt16: return Boxes.Box(UInt16Value); case ConstantValueTypeDiscriminator.Int32: return Boxes.Box(Int32Value); case ConstantValueTypeDiscriminator.UInt32: return Boxes.Box(UInt32Value); case ConstantValueTypeDiscriminator.Int64: return Boxes.Box(Int64Value); case ConstantValueTypeDiscriminator.UInt64: return Boxes.Box(UInt64Value); case ConstantValueTypeDiscriminator.NInt: return Boxes.Box(Int32Value); case ConstantValueTypeDiscriminator.NUInt: return Boxes.Box(UInt32Value); case ConstantValueTypeDiscriminator.Char: return Boxes.Box(CharValue); case ConstantValueTypeDiscriminator.Boolean: return Boxes.Box(BooleanValue); case ConstantValueTypeDiscriminator.Single: return Boxes.Box(SingleValue); case ConstantValueTypeDiscriminator.Double: return Boxes.Box(DoubleValue); case ConstantValueTypeDiscriminator.Decimal: return Boxes.Box(DecimalValue); case ConstantValueTypeDiscriminator.DateTime: return DateTimeValue; case ConstantValueTypeDiscriminator.String: return StringValue; default: throw ExceptionUtilities.UnexpectedValue(this.Discriminator); } } } public static bool IsIntegralType(ConstantValueTypeDiscriminator discriminator) { switch (discriminator) { case ConstantValueTypeDiscriminator.SByte: case ConstantValueTypeDiscriminator.Byte: case ConstantValueTypeDiscriminator.Int16: case ConstantValueTypeDiscriminator.UInt16: case ConstantValueTypeDiscriminator.Int32: case ConstantValueTypeDiscriminator.UInt32: case ConstantValueTypeDiscriminator.Int64: case ConstantValueTypeDiscriminator.UInt64: case ConstantValueTypeDiscriminator.NInt: case ConstantValueTypeDiscriminator.NUInt: return true; default: return false; } } public bool IsIntegral { get { return IsIntegralType(this.Discriminator); } } public bool IsNegativeNumeric { get { switch (this.Discriminator) { case ConstantValueTypeDiscriminator.SByte: return SByteValue < 0; case ConstantValueTypeDiscriminator.Int16: return Int16Value < 0; case ConstantValueTypeDiscriminator.Int32: case ConstantValueTypeDiscriminator.NInt: return Int32Value < 0; case ConstantValueTypeDiscriminator.Int64: return Int64Value < 0; case ConstantValueTypeDiscriminator.Single: return SingleValue < 0; case ConstantValueTypeDiscriminator.Double: return DoubleValue < 0; case ConstantValueTypeDiscriminator.Decimal: return DecimalValue < 0; default: return false; } } } public bool IsNumeric { get { switch (this.Discriminator) { case ConstantValueTypeDiscriminator.SByte: case ConstantValueTypeDiscriminator.Int16: case ConstantValueTypeDiscriminator.Int32: case ConstantValueTypeDiscriminator.Int64: case ConstantValueTypeDiscriminator.Single: case ConstantValueTypeDiscriminator.Double: case ConstantValueTypeDiscriminator.Decimal: case ConstantValueTypeDiscriminator.Byte: case ConstantValueTypeDiscriminator.UInt16: case ConstantValueTypeDiscriminator.UInt32: case ConstantValueTypeDiscriminator.UInt64: case ConstantValueTypeDiscriminator.NInt: case ConstantValueTypeDiscriminator.NUInt: return true; default: return false; } } } public static bool IsUnsignedIntegralType(ConstantValueTypeDiscriminator discriminator) { switch (discriminator) { case ConstantValueTypeDiscriminator.Byte: case ConstantValueTypeDiscriminator.UInt16: case ConstantValueTypeDiscriminator.UInt32: case ConstantValueTypeDiscriminator.UInt64: case ConstantValueTypeDiscriminator.NUInt: return true; default: return false; } } public bool IsUnsigned { get { return IsUnsignedIntegralType(this.Discriminator); } } public static bool IsBooleanType(ConstantValueTypeDiscriminator discriminator) { return discriminator == ConstantValueTypeDiscriminator.Boolean; } public bool IsBoolean { get { return this.Discriminator == ConstantValueTypeDiscriminator.Boolean; } } public static bool IsCharType(ConstantValueTypeDiscriminator discriminator) { return discriminator == ConstantValueTypeDiscriminator.Char; } public bool IsChar { get { return this.Discriminator == ConstantValueTypeDiscriminator.Char; } } public static bool IsStringType(ConstantValueTypeDiscriminator discriminator) { return discriminator == ConstantValueTypeDiscriminator.String; } [MemberNotNullWhen(true, nameof(StringValue))] public bool IsString { get { return this.Discriminator == ConstantValueTypeDiscriminator.String; } } public static bool IsDecimalType(ConstantValueTypeDiscriminator discriminator) { return discriminator == ConstantValueTypeDiscriminator.Decimal; } public bool IsDecimal { get { return this.Discriminator == ConstantValueTypeDiscriminator.Decimal; } } public static bool IsDateTimeType(ConstantValueTypeDiscriminator discriminator) { return discriminator == ConstantValueTypeDiscriminator.DateTime; } public bool IsDateTime { get { return this.Discriminator == ConstantValueTypeDiscriminator.DateTime; } } public static bool IsFloatingType(ConstantValueTypeDiscriminator discriminator) { return discriminator == ConstantValueTypeDiscriminator.Double || discriminator == ConstantValueTypeDiscriminator.Single; } public bool IsFloating { get { return this.Discriminator == ConstantValueTypeDiscriminator.Double || this.Discriminator == ConstantValueTypeDiscriminator.Single; } } public bool IsBad { get { return this.Discriminator == ConstantValueTypeDiscriminator.Bad; } } public bool IsNull { get { return ReferenceEquals(this, Null); } } public bool IsNothing { get { return ReferenceEquals(this, Nothing); } } public void Serialize(BlobBuilder writer) { switch (this.Discriminator) { case ConstantValueTypeDiscriminator.Boolean: writer.WriteBoolean(this.BooleanValue); break; case ConstantValueTypeDiscriminator.SByte: writer.WriteSByte(this.SByteValue); break; case ConstantValueTypeDiscriminator.Byte: writer.WriteByte(this.ByteValue); break; case ConstantValueTypeDiscriminator.Char: case ConstantValueTypeDiscriminator.Int16: writer.WriteInt16(this.Int16Value); break; case ConstantValueTypeDiscriminator.UInt16: writer.WriteUInt16(this.UInt16Value); break; case ConstantValueTypeDiscriminator.Single: writer.WriteSingle(this.SingleValue); break; case ConstantValueTypeDiscriminator.Int32: writer.WriteInt32(this.Int32Value); break; case ConstantValueTypeDiscriminator.UInt32: writer.WriteUInt32(this.UInt32Value); break; case ConstantValueTypeDiscriminator.Double: writer.WriteDouble(this.DoubleValue); break; case ConstantValueTypeDiscriminator.Int64: writer.WriteInt64(this.Int64Value); break; case ConstantValueTypeDiscriminator.UInt64: writer.WriteUInt64(this.UInt64Value); break; default: throw ExceptionUtilities.UnexpectedValue(this.Discriminator); } } public override string ToString() { string? valueToDisplay = this.GetValueToDisplay(); return String.Format("{0}({1}: {2})", this.GetType().Name, valueToDisplay, this.Discriminator); } public virtual string ToString(string? format, IFormatProvider? provider) { return Discriminator switch { ConstantValueTypeDiscriminator.SByte => SByteValue.ToString(provider), ConstantValueTypeDiscriminator.Byte => ByteValue.ToString(provider), ConstantValueTypeDiscriminator.Int16 => Int16Value.ToString(provider), ConstantValueTypeDiscriminator.UInt16 => UInt16Value.ToString(provider), ConstantValueTypeDiscriminator.NInt or ConstantValueTypeDiscriminator.Int32 => Int32Value.ToString(provider), ConstantValueTypeDiscriminator.NUInt or ConstantValueTypeDiscriminator.UInt32 => UInt32Value.ToString(provider), ConstantValueTypeDiscriminator.UInt64 => UInt64Value.ToString(provider), ConstantValueTypeDiscriminator.Int64 => Int64Value.ToString(provider), ConstantValueTypeDiscriminator.Char => CharValue.ToString(provider), ConstantValueTypeDiscriminator.Boolean => BooleanValue.ToString(provider), ConstantValueTypeDiscriminator.Single => SingleValue.ToString(provider), ConstantValueTypeDiscriminator.Double => DoubleValue.ToString(provider), ConstantValueTypeDiscriminator.Decimal => DecimalValue.ToString(provider), ConstantValueTypeDiscriminator.DateTime => DateTimeValue.ToString(provider), _ => throw ExceptionUtilities.UnexpectedValue(Discriminator) }; } internal virtual string? GetValueToDisplay() { return this.Value?.ToString(); } internal bool IsIntegralValueZeroOrOne(out bool isOne) { if (IsDefaultValue) { isOne = false; } else if (IsOne) { isOne = true; } else { isOne = default; return false; } return IsIntegral || IsBoolean || IsChar; } // equal constants must have matching discriminators // derived types override this if equivalence is more than just discriminators match. // singletons also override this since they only need a reference compare. public virtual bool Equals(ConstantValue? other) { if (ReferenceEquals(other, this)) { return true; } if (ReferenceEquals(other, null)) { return false; } return this.Discriminator == other.Discriminator; } public static bool operator ==(ConstantValue? left, ConstantValue? right) { if (ReferenceEquals(right, left)) { return true; } if (ReferenceEquals(left, null)) { return false; } return left.Equals(right); } public static bool operator !=(ConstantValue? left, ConstantValue? right) { return !(left == right); } public override int GetHashCode() { return ((int)this.Discriminator).GetHashCode(); } public override bool Equals(object? obj) { return this.Equals(obj as ConstantValue); } } }