| File: System\Formats\Nrbf\ClassRecord.cs | Web Access |
| Project: src\runtime\src\libraries\System.Formats.Nrbf\src\System.Formats.Nrbf.csproj (System.Formats.Nrbf) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.Reflection.Metadata; namespace System.Formats.Nrbf; /// <summary> /// Defines the core behavior for NRBF class records and provides a base for derived classes. /// </summary> public abstract class ClassRecord : SerializationRecord { private protected ClassRecord(ClassInfo classInfo, MemberTypeInfo memberTypeInfo) { ClassInfo = classInfo; MemberTypeInfo = memberTypeInfo; MemberValues = []; } /// <inheritdoc /> public override TypeName TypeName => ClassInfo.TypeName; /// <summary> /// Gets the names of the serialized members. /// </summary> /// <value>The names of the serialized members.</value> public IEnumerable<string> MemberNames => ClassInfo.MemberNames.Keys; /// <inheritdoc /> public override SerializationRecordId Id => ClassInfo.Id; internal ClassInfo ClassInfo { get; } internal MemberTypeInfo MemberTypeInfo { get; } internal int ExpectedValuesCount => MemberTypeInfo.Infos.Count; internal List<object?> MemberValues { get; } /// <summary> /// Checks if the member with the given name was present in the payload. /// </summary> /// <param name="memberName">The name of the member.</param> /// <returns><see langword="true" /> if the member was present, otherwise <see langword="false" />.</returns> /// <remarks> /// It's recommended to use this method when dealing with payload that might contain /// different versions of the same type. /// </remarks> public bool HasMember(string memberName) => ClassInfo.MemberNames.ContainsKey(memberName); /// <summary> /// Retrieves the value of the provided <paramref name="memberName"/>. /// </summary> /// <param name="memberName">The name of the member.</param> /// <returns>The value.</returns> /// <exception cref="KeyNotFoundException"><paramref name="memberName" /> does not refer to a known member. You can use <see cref="HasMember(string)"/> to check if a given member exists.</exception> /// <exception cref="InvalidOperationException">The member with the specified name has a value of a different type.</exception> public ClassRecord? GetClassRecord(string memberName) => GetMember<ClassRecord>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public string? GetString(string memberName) => GetMember<string>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public bool GetBoolean(string memberName) => GetMember<bool>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public byte GetByte(string memberName) => GetMember<byte>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public sbyte GetSByte(string memberName) => GetMember<sbyte>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public short GetInt16(string memberName) => GetMember<short>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public ushort GetUInt16(string memberName) => GetMember<ushort>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public char GetChar(string memberName) => GetMember<char>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public int GetInt32(string memberName) => GetMember<int>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public uint GetUInt32(string memberName) => GetMember<uint>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public float GetSingle(string memberName) => GetMember<float>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public long GetInt64(string memberName) => GetMember<long>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public ulong GetUInt64(string memberName) => GetMember<ulong>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public double GetDouble(string memberName) => GetMember<double>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public decimal GetDecimal(string memberName) => GetMember<decimal>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public TimeSpan GetTimeSpan(string memberName) => GetMember<TimeSpan>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public DateTime GetDateTime(string memberName) => GetMember<DateTime>(memberName); /// <returns> /// <para>For primitive types like <see cref="int"/>, <see langword="string"/>, or <see cref="DateTime"/>, returns their value.</para> /// <para>For nulls, returns a null.</para> /// <para>For other types that are not arrays, returns an instance of <see cref="ClassRecord"/>.</para> /// <para>For single-dimensional arrays, returns <see cref="SZArrayRecord{T}"/> where the generic type is the primitive type or <see cref="ClassRecord"/>.</para> /// <para>For jagged and multi-dimensional arrays, returns an instance of <see cref="ArrayRecord"/>.</para> /// </returns> /// <inheritdoc cref="GetClassRecord(string)"/> public object? GetRawValue(string memberName) => GetMember<object>(memberName); /// <inheritdoc cref="GetClassRecord(string)"/> public ArrayRecord? GetArrayRecord(string memberName) => GetMember<ArrayRecord>(memberName); /// <summary> /// Retrieves the <see cref="SerializationRecord" /> of the provided <paramref name="memberName"/>. /// </summary> /// <param name="memberName">The name of the field.</param> /// <returns>The serialization record, which can be any of <see cref="PrimitiveTypeRecord{T}"/>, /// <see cref="ClassRecord"/>, <see cref="ArrayRecord"/>, or <see langword="null" />. /// </returns> /// <exception cref="KeyNotFoundException"><paramref name="memberName" /> does not refer to a known member. You can use <see cref="HasMember(string)"/> to check if given member exists.</exception> /// <exception cref="InvalidOperationException">The specified member is not a <see cref="SerializationRecord"/>, but just a raw primitive value.</exception> public SerializationRecord? GetSerializationRecord(string memberName) => MemberValues[ClassInfo.MemberNames[memberName]] switch { null or NullsRecord => null, MemberReferenceRecord referenceRecord => referenceRecord.GetReferencedRecord(), SerializationRecord serializationRecord => serializationRecord, _ => throw new InvalidOperationException(SR.Format(SR.Serialization_MemberTypeMismatchException, memberName)) }; private T? GetMember<T>(string memberName) { int index = ClassInfo.MemberNames[memberName]; object? value = MemberValues[index]; if (value is SerializationRecord record) { value = record.GetValue(); } return value is null ? default : value is not T ? throw new InvalidOperationException(SR.Format(SR.Serialization_MemberTypeMismatchException, memberName)) : (T)value!; } internal abstract (AllowedRecordTypes allowed, PrimitiveType primitiveType) GetNextAllowedRecordType(); internal override void HandleNextRecord(SerializationRecord nextRecord, NextInfo info) { // ObjectNullRecord is the only valid null record that can represent class record members, // even for multiple nulls provided in a row. Debug.Assert(nextRecord is not (ObjectNullMultiple256Record or ObjectNullMultipleRecord)); HandleNextValue(nextRecord, info); } internal override void HandleNextValue(object value, NextInfo info) { MemberValues.Add(value); if (MemberValues.Count < ExpectedValuesCount) { (AllowedRecordTypes allowed, PrimitiveType primitiveType) = GetNextAllowedRecordType(); info.Stack.Push(info.With(allowed, primitiveType)); } } }