File: System\Runtime\Serialization\ClassDataContract.cs
Web Access
Project: src\src\libraries\System.Private.DataContractSerialization\src\System.Private.DataContractSerialization.csproj (System.Private.DataContractSerialization)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Threading;
using System.Xml;
 
using DataContractDictionary = System.Collections.Generic.Dictionary<System.Xml.XmlQualifiedName, System.Runtime.Serialization.DataContracts.DataContract>;
 
namespace System.Runtime.Serialization.DataContracts
{
    internal sealed class ClassDataContract : DataContract
    {
        internal const string ContractTypeString = nameof(ClassDataContract);
        public override string? ContractType => ContractTypeString;
 
        public XmlDictionaryString[]? ContractNamespaces;
 
        public XmlDictionaryString[]? MemberNames;
 
        internal XmlDictionaryString[]? MemberNamespaces;
 
        private XmlDictionaryString?[]? _childElementNamespaces;
 
        private ClassDataContractCriticalHelper _helper;
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal ClassDataContract(Type type) : base(new ClassDataContractCriticalHelper(type))
        {
            InitClassDataContract();
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private ClassDataContract(Type type, XmlDictionaryString ns, string[] memberNames) : base(new ClassDataContractCriticalHelper(type, ns, memberNames))
        {
            InitClassDataContract();
        }
 
        [MemberNotNull(nameof(_helper))]
        private void InitClassDataContract()
        {
            _helper = (base.Helper as ClassDataContractCriticalHelper)!;
            ContractNamespaces = _helper.ContractNamespaces;
            MemberNames = _helper.MemberNames;
            MemberNamespaces = _helper.MemberNamespaces;
        }
 
        public override DataContract? BaseContract
        {
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            get => BaseClassContract;
        }
 
        internal ClassDataContract? BaseClassContract
        {
            get => _helper.BaseClassContract;
            set => _helper.BaseClassContract = value;
        }
 
        internal List<DataMember>? Members
        {
            get => _helper.Members;
            set => _helper.Members = value;
        }
 
        public override ReadOnlyCollection<DataMember> DataMembers => (Members == null) ? ReadOnlyCollection<DataMember>.Empty : Members.AsReadOnly();
 
        internal XmlDictionaryString?[]? ChildElementNamespaces
        {
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            get
            {
                if (_childElementNamespaces == null)
                {
                    lock (this)
                    {
                        if (_childElementNamespaces == null)
                        {
                            if (_helper.ChildElementNamespaces == null)
                            {
                                XmlDictionaryString?[]? tempChildElementamespaces = CreateChildElementNamespaces();
                                Interlocked.MemoryBarrier();
                                _helper.ChildElementNamespaces = tempChildElementamespaces;
                            }
                            _childElementNamespaces = _helper.ChildElementNamespaces;
                        }
                    }
                }
                return _childElementNamespaces;
            }
        }
 
        internal MethodInfo? OnSerializing => _helper.OnSerializing;
 
        internal MethodInfo? OnSerialized => _helper.OnSerialized;
 
        internal MethodInfo? OnDeserializing => _helper.OnDeserializing;
 
        internal MethodInfo? OnDeserialized => _helper.OnDeserialized;
 
        internal MethodInfo? ExtensionDataSetMethod => _helper.ExtensionDataSetMethod;
 
        public override DataContractDictionary? KnownDataContracts
        {
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            get => _helper.KnownDataContracts;
            internal set => _helper.KnownDataContracts = value;
        }
 
        public override bool IsISerializable
        {
            get => _helper.IsISerializable;
            internal set => _helper.IsISerializable = value;
        }
 
        internal bool IsNonAttributedType => _helper.IsNonAttributedType;
 
        internal bool HasExtensionData => _helper.HasExtensionData;
 
        internal string? SerializationExceptionMessage => _helper.SerializationExceptionMessage;
        internal string? DeserializationExceptionMessage => _helper.DeserializationExceptionMessage;
 
        internal bool IsReadOnlyContract => DeserializationExceptionMessage != null;
 
        internal ConstructorInfo? GetISerializableConstructor()
        {
            return _helper.GetISerializableConstructor();
        }
 
        private ConstructorInfo? _nonAttributedTypeConstructor;
 
        internal ConstructorInfo? GetNonAttributedTypeConstructor()
        {
            if (_nonAttributedTypeConstructor == null)
            {
                // Cache the ConstructorInfo to improve performance.
                _nonAttributedTypeConstructor = _helper.GetNonAttributedTypeConstructor();
            }
 
            return _nonAttributedTypeConstructor;
        }
 
        private Func<object>? _makeNewInstance;
 
        [UnconditionalSuppressMessage("AOT Analysis", "IL3050:RequiresDynamicCodeAttribute",
            Justification = "Fields cannot be annotated, annotating the use instead")]
        private Func<object> MakeNewInstance => _makeNewInstance ??= FastInvokerBuilder.GetMakeNewInstanceFunc(UnderlyingType);
 
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        internal bool CreateNewInstanceViaDefaultConstructor([NotNullWhen(true)] out object? obj)
        {
            ConstructorInfo? ci = GetNonAttributedTypeConstructor();
            if (ci == null || UnderlyingType == Globals.TypeOfSchemaDefinedType)
            {
                obj = null;
                return false;
            }
 
            if (ci.IsPublic)
            {
                // Optimization for calling public default ctor.
                obj = MakeNewInstance();
            }
            else
            {
                obj = ci.Invoke(Array.Empty<object>());
            }
 
            return true;
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private XmlFormatClassWriterDelegate CreateXmlFormatWriterDelegate()
        {
            Debug.Assert(UnderlyingType != Globals.TypeOfSchemaDefinedType);
            return new XmlFormatWriterGenerator().GenerateClassWriter(this);
        }
 
        internal XmlFormatClassWriterDelegate XmlFormatWriterDelegate
        {
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            get
            {
                if (_helper.XmlFormatWriterDelegate == null)
                {
                    lock (this)
                    {
                        if (_helper.XmlFormatWriterDelegate == null)
                        {
                            XmlFormatClassWriterDelegate tempDelegate = CreateXmlFormatWriterDelegate();
                            Interlocked.MemoryBarrier();
                            _helper.XmlFormatWriterDelegate = tempDelegate;
                        }
                    }
                }
                return _helper.XmlFormatWriterDelegate;
            }
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private XmlFormatClassReaderDelegate CreateXmlFormatReaderDelegate()
        {
            Debug.Assert(UnderlyingType != Globals.TypeOfSchemaDefinedType);
            return new XmlFormatReaderGenerator().GenerateClassReader(this);
        }
 
        internal XmlFormatClassReaderDelegate XmlFormatReaderDelegate
        {
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            get
            {
                if (_helper.XmlFormatReaderDelegate == null)
                {
                    lock (this)
                    {
                        if (_helper.XmlFormatReaderDelegate == null)
                        {
                            if (IsReadOnlyContract)
                            {
                                ThrowInvalidDataContractException(DeserializationExceptionMessage, type: null);
                            }
                            XmlFormatClassReaderDelegate tempDelegate = CreateXmlFormatReaderDelegate();
                            Interlocked.MemoryBarrier();
                            _helper.XmlFormatReaderDelegate = tempDelegate;
                        }
                    }
                }
                return _helper.XmlFormatReaderDelegate;
            }
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal static ClassDataContract CreateClassDataContractForKeyValue(Type type, XmlDictionaryString ns, string[] memberNames)
        {
            return new ClassDataContract(type, ns, memberNames);
        }
 
        internal static void CheckAndAddMember(List<DataMember> members, DataMember memberContract, Dictionary<string, DataMember> memberNamesTable)
        {
            if (memberNamesTable.TryGetValue(memberContract.Name, out DataMember? existingMemberContract))
            {
                Type declaringType = memberContract.MemberInfo.DeclaringType!;
                DataContract.ThrowInvalidDataContractException(
                    SR.Format((declaringType.IsEnum ? SR.DupEnumMemberValue : SR.DupMemberName),
                        existingMemberContract.MemberInfo.Name,
                        memberContract.MemberInfo.Name,
                        DataContract.GetClrTypeFullName(declaringType),
                        memberContract.Name),
                    declaringType);
            }
            memberNamesTable.Add(memberContract.Name, memberContract);
            members.Add(memberContract);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal static XmlDictionaryString? GetChildNamespaceToDeclare(DataContract dataContract, Type childType, XmlDictionary dictionary)
        {
            childType = DataContract.UnwrapNullableType(childType);
            if (!childType.IsEnum && !Globals.TypeOfIXmlSerializable.IsAssignableFrom(childType)
                && DataContract.GetBuiltInDataContract(childType) == null && childType != Globals.TypeOfDBNull)
            {
                string ns = DataContract.GetXmlName(childType).Namespace;
                if (ns.Length > 0 && ns != dataContract.Namespace.Value)
                    return dictionary.Add(ns);
            }
            return null;
        }
 
        private static bool IsArraySegment(Type t)
        {
            return t.IsGenericType && (t.GetGenericTypeDefinition() == typeof(ArraySegment<>));
        }
 
        /// <SecurityNote>
        /// RequiresReview - callers may need to depend on isNonAttributedType for a security decision
        ///            isNonAttributedType must be calculated correctly
        ///            IsNonAttributedTypeValidForSerialization is used as part of the isNonAttributedType calculation and
        ///            is therefore marked SRR
        /// Safe - does not let caller influence isNonAttributedType calculation; no harm in leaking value
        /// </SecurityNote>
        internal static bool IsNonAttributedTypeValidForSerialization(
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.Interfaces)]
            Type type)
        {
            if (type.IsArray)
                return false;
 
            if (type.IsEnum)
                return false;
 
            if (type.IsGenericParameter)
                return false;
 
            if (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type))
                return false;
 
            if (type.IsPointer)
                return false;
 
            if (type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false))
                return false;
 
            if (!IsArraySegment(type))
            {
                foreach (Type interfaceType in type.GetInterfaces())
                {
                    if (CollectionDataContract.IsCollectionInterface(interfaceType))
                        return false;
                }
            }
 
#pragma warning disable SYSLIB0050 // Type.IsSerializable is obsolete
            if (type.IsSerializable)
                return false;
#pragma warning restore SYSLIB0050
 
            if (Globals.TypeOfISerializable.IsAssignableFrom(type))
                return false;
 
            if (type.IsDefined(Globals.TypeOfDataContractAttribute, false))
                return false;
 
            if (type == Globals.TypeOfExtensionDataObject)
                return false;
 
            if (type.IsValueType)
                return type.IsVisible;
 
            return (type.IsVisible &&
                type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, Type.EmptyTypes) != null);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private XmlDictionaryString?[]? CreateChildElementNamespaces()
        {
            if (Members == null)
                return null;
 
            XmlDictionaryString?[]? baseChildElementNamespaces = null;
            if (BaseClassContract != null)
                baseChildElementNamespaces = BaseClassContract.ChildElementNamespaces;
            int baseChildElementNamespaceCount = (baseChildElementNamespaces != null) ? baseChildElementNamespaces.Length : 0;
            XmlDictionaryString?[] childElementNamespaces = new XmlDictionaryString?[Members.Count + baseChildElementNamespaceCount];
            if (baseChildElementNamespaceCount > 0)
                Array.Copy(baseChildElementNamespaces!, childElementNamespaces, baseChildElementNamespaces!.Length);
 
            XmlDictionary dictionary = new XmlDictionary();
            for (int i = 0; i < Members.Count; i++)
            {
                childElementNamespaces[i + baseChildElementNamespaceCount] = GetChildNamespaceToDeclare(this, Members[i].MemberType, dictionary);
            }
 
            return childElementNamespaces;
        }
 
        private void EnsureMethodsImported()
        {
            _helper.EnsureMethodsImported();
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context)
        {
            Debug.Assert(context != null);
            XmlFormatWriterDelegate(xmlWriter, obj, context, this);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context)
        {
            xmlReader.Read();
            object? o = XmlFormatReaderDelegate(xmlReader, context, MemberNames, MemberNamespaces);
            xmlReader.ReadEndElement();
            return o;
        }
 
        /// <SecurityNote>
        /// Review - calculates whether this class requires MemberAccessPermission for deserialization.
        ///          since this information is used to determine whether to give the generated code access
        ///          permissions to private members, any changes to the logic should be reviewed.
        /// </SecurityNote>
        internal bool RequiresMemberAccessForRead(SecurityException? securityException)
        {
            EnsureMethodsImported();
            if (!IsTypeVisible(UnderlyingType))
            {
                if (securityException != null)
                {
                    throw new SecurityException(SR.Format(
                                SR.PartialTrustDataContractTypeNotPublic,
                                DataContract.GetClrTypeFullName(UnderlyingType)),
                            securityException);
                }
                return true;
            }
            if (BaseClassContract != null && BaseClassContract.RequiresMemberAccessForRead(securityException))
                return true;
 
            if (ConstructorRequiresMemberAccess(GetISerializableConstructor()))
            {
                if (securityException != null)
                {
                    throw new SecurityException(SR.Format(
                                SR.PartialTrustIXmlSerialzableNoPublicConstructor,
                                DataContract.GetClrTypeFullName(UnderlyingType)),
                            securityException);
                }
                return true;
            }
 
            if (ConstructorRequiresMemberAccess(GetNonAttributedTypeConstructor()))
            {
                if (securityException != null)
                {
                    throw new SecurityException(SR.Format(
                                SR.PartialTrustNonAttributedSerializableTypeNoPublicConstructor,
                                DataContract.GetClrTypeFullName(UnderlyingType)),
                            securityException);
                }
                return true;
            }
 
            if (MethodRequiresMemberAccess(OnDeserializing))
            {
                if (securityException != null)
                {
                    throw new SecurityException(SR.Format(
                                SR.PartialTrustDataContractOnDeserializingNotPublic,
                                DataContract.GetClrTypeFullName(UnderlyingType),
                                OnDeserializing!.Name),
                            securityException);
                }
                return true;
            }
 
            if (MethodRequiresMemberAccess(OnDeserialized))
            {
                if (securityException != null)
                {
                    throw new SecurityException(SR.Format(
                                SR.PartialTrustDataContractOnDeserializedNotPublic,
                                DataContract.GetClrTypeFullName(UnderlyingType),
                                OnDeserialized!.Name),
                            securityException);
                }
                return true;
            }
 
            if (Members != null)
            {
                for (int i = 0; i < Members.Count; i++)
                {
                    if (Members[i].RequiresMemberAccessForSet())
                    {
                        if (securityException != null)
                        {
                            if (Members[i].MemberInfo is FieldInfo)
                            {
                                throw new SecurityException(SR.Format(
                                            SR.PartialTrustDataContractFieldSetNotPublic,
                                            DataContract.GetClrTypeFullName(UnderlyingType),
                                            Members[i].MemberInfo.Name),
                                        securityException);
                            }
                            else
                            {
                                throw new SecurityException(SR.Format(
                                            SR.PartialTrustDataContractPropertySetNotPublic,
                                            DataContract.GetClrTypeFullName(UnderlyingType),
                                            Members[i].MemberInfo.Name),
                                        securityException);
                            }
                        }
                        return true;
                    }
                }
            }
 
            return false;
        }
 
        /// <SecurityNote>
        /// Review - calculates whether this class requires MemberAccessPermission for serialization.
        ///          since this information is used to determine whether to give the generated code access
        ///          permissions to private members, any changes to the logic should be reviewed.
        /// </SecurityNote>
        internal bool RequiresMemberAccessForWrite(SecurityException? securityException)
        {
            EnsureMethodsImported();
 
            if (!IsTypeVisible(UnderlyingType))
            {
                if (securityException != null)
                {
                    throw new SecurityException(SR.Format(
                                SR.PartialTrustDataContractTypeNotPublic,
                                DataContract.GetClrTypeFullName(UnderlyingType)),
                            securityException);
                }
                return true;
            }
 
            if (BaseClassContract != null && BaseClassContract.RequiresMemberAccessForWrite(securityException))
                return true;
 
            if (MethodRequiresMemberAccess(OnSerializing))
            {
                if (securityException != null)
                {
                    throw new SecurityException(SR.Format(
                                SR.PartialTrustDataContractOnSerializingNotPublic,
                                DataContract.GetClrTypeFullName(UnderlyingType),
                                OnSerializing!.Name),
                            securityException);
                }
                return true;
            }
 
            if (MethodRequiresMemberAccess(OnSerialized))
            {
                if (securityException != null)
                {
                    throw new SecurityException(SR.Format(
                                SR.PartialTrustDataContractOnSerializedNotPublic,
                                DataContract.GetClrTypeFullName(UnderlyingType),
                                OnSerialized!.Name),
                            securityException);
                }
                return true;
            }
 
            if (Members != null)
            {
                for (int i = 0; i < Members.Count; i++)
                {
                    if (Members[i].RequiresMemberAccessForGet())
                    {
                        if (securityException != null)
                        {
                            if (Members[i].MemberInfo is FieldInfo)
                            {
                                throw new SecurityException(SR.Format(
                                            SR.PartialTrustDataContractFieldGetNotPublic,
                                            DataContract.GetClrTypeFullName(UnderlyingType),
                                            Members[i].MemberInfo.Name),
                                        securityException);
                            }
                            else
                            {
                                throw new SecurityException(SR.Format(
                                            SR.PartialTrustDataContractPropertyGetNotPublic,
                                            DataContract.GetClrTypeFullName(UnderlyingType),
                                            Members[i].MemberInfo.Name),
                                        securityException);
                            }
                        }
                        return true;
                    }
                }
            }
 
            return false;
        }
 
        private sealed class ClassDataContractCriticalHelper : DataContract.DataContractCriticalHelper
        {
            private static Type[]? s_serInfoCtorArgs;
 
            private ClassDataContract? _baseContract;
            private List<DataMember>? _members;
            private MethodInfo? _onSerializing, _onSerialized;
            private MethodInfo? _onDeserializing, _onDeserialized;
            private MethodInfo? _extensionDataSetMethod;
            private DataContractDictionary? _knownDataContracts;
            private string? _serializationExceptionMessage;
            private bool _isKnownTypeAttributeChecked;
            private bool _isMethodChecked;
 
            /// <SecurityNote>
            /// in serialization/deserialization we base the decision whether to Demand SerializationFormatter permission on this value and isNonAttributedType
            /// </SecurityNote>
            private bool _isNonAttributedType;
 
            /// <SecurityNote>
            /// in serialization/deserialization we base the decision whether to Demand SerializationFormatter permission on this value and hasDataContract
            /// </SecurityNote>
            private bool _hasDataContract;
            private readonly bool _hasExtensionData;
 
            internal XmlDictionaryString[]? ContractNamespaces;
            internal XmlDictionaryString[]? MemberNames;
            internal XmlDictionaryString[]? MemberNamespaces;
 
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            internal ClassDataContractCriticalHelper([DynamicallyAccessedMembers(DataContractPreserveMemberTypes)]
                Type type) : base(type)
            {
                XmlQualifiedName xmlName = GetXmlNameAndSetHasDataContract(type);
                if (type == Globals.TypeOfDBNull)
                {
                    XmlName = xmlName;
                    _members = new List<DataMember>();
                    XmlDictionary dictionary = new XmlDictionary(2);
                    Name = dictionary.Add(XmlName.Name);
                    Namespace = dictionary.Add(XmlName.Namespace);
                    ContractNamespaces = MemberNames = MemberNamespaces = Array.Empty<XmlDictionaryString>();
                    EnsureMethodsImported();
                    return;
                }
                Type? baseType = type.BaseType;
                IsISerializable = (Globals.TypeOfISerializable.IsAssignableFrom(type));
                SetIsNonAttributedType(type);
                if (IsISerializable)
                {
                    if (HasDataContract)
                        throw new InvalidDataContractException(SR.Format(SR.ISerializableCannotHaveDataContract, DataContract.GetClrTypeFullName(type)));
#pragma warning disable SYSLIB0050 // Type.IsSerializable is obsolete
                    if (baseType != null && !(baseType.IsSerializable && Globals.TypeOfISerializable.IsAssignableFrom(baseType)))
                        baseType = null;
#pragma warning restore SYSLIB0050
                }
                IsValueType = type.IsValueType;
                if (baseType != null && baseType != Globals.TypeOfObject && baseType != Globals.TypeOfValueType && baseType != Globals.TypeOfUri)
                {
                    DataContract baseContract = DataContract.GetDataContract(baseType);
                    if (baseContract is CollectionDataContract collectionDC)
                    {
                        BaseClassContract = collectionDC.SharedTypeContract as ClassDataContract;
                    }
                    else
                    {
                        BaseClassContract = baseContract as ClassDataContract;
                    }
 
                    if (BaseClassContract != null && BaseClassContract.IsNonAttributedType && !_isNonAttributedType)
                    {
                        throw new InvalidDataContractException(SR.Format(SR.AttributedTypesCannotInheritFromNonAttributedSerializableTypes,
                            DataContract.GetClrTypeFullName(type), DataContract.GetClrTypeFullName(baseType)));
                    }
                }
                else
                {
                    BaseClassContract = null;
                }
 
                _hasExtensionData = (Globals.TypeOfIExtensibleDataObject.IsAssignableFrom(type));
                if (_hasExtensionData && !HasDataContract && !IsNonAttributedType)
                {
                    throw new InvalidDataContractException(SR.Format(SR.OnlyDataContractTypesCanHaveExtensionData, DataContract.GetClrTypeFullName(type)));
                }
 
                if (IsISerializable)
                {
                    SetDataContractName(xmlName);
                }
                else
                {
                    XmlName = xmlName;
                    ImportDataMembers();
                    XmlDictionary dictionary = new XmlDictionary(2 + Members.Count);
                    Name = dictionary.Add(XmlName.Name);
                    Namespace = dictionary.Add(XmlName.Namespace);
 
                    int baseMemberCount = 0;
                    int baseContractCount = 0;
                    if (BaseClassContract == null)
                    {
                        MemberNames = new XmlDictionaryString[Members.Count];
                        MemberNamespaces = new XmlDictionaryString[Members.Count];
                        ContractNamespaces = new XmlDictionaryString[1];
                    }
                    else
                    {
                        if (BaseClassContract.IsReadOnlyContract)
                        {
                            _serializationExceptionMessage = BaseClassContract.SerializationExceptionMessage;
                        }
                        baseMemberCount = BaseClassContract.MemberNames!.Length;
                        MemberNames = new XmlDictionaryString[Members.Count + baseMemberCount];
                        Array.Copy(BaseClassContract.MemberNames, MemberNames, baseMemberCount);
                        MemberNamespaces = new XmlDictionaryString[Members.Count + baseMemberCount];
                        Array.Copy(BaseClassContract.MemberNamespaces!, MemberNamespaces, baseMemberCount);
                        baseContractCount = BaseClassContract.ContractNamespaces!.Length;
                        ContractNamespaces = new XmlDictionaryString[1 + baseContractCount];
                        Array.Copy(BaseClassContract.ContractNamespaces, ContractNamespaces, baseContractCount);
                    }
                    ContractNamespaces[baseContractCount] = Namespace;
                    for (int i = 0; i < Members.Count; i++)
                    {
                        MemberNames[i + baseMemberCount] = dictionary.Add(Members[i].Name);
                        MemberNamespaces[i + baseMemberCount] = Namespace;
                    }
                }
 
                EnsureMethodsImported();
            }
 
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            internal ClassDataContractCriticalHelper(
                [DynamicallyAccessedMembers(DataContractPreserveMemberTypes)]
                Type type, XmlDictionaryString ns, string[] memberNames) : base(type)
            {
                XmlName = new XmlQualifiedName(GetXmlNameAndSetHasDataContract(type).Name, ns.Value);
                ImportDataMembers();
                XmlDictionary dictionary = new XmlDictionary(1 + Members.Count);
                Name = dictionary.Add(XmlName.Name);
                Namespace = ns;
                ContractNamespaces = new XmlDictionaryString[] { Namespace };
                MemberNames = new XmlDictionaryString[Members.Count];
                MemberNamespaces = new XmlDictionaryString[Members.Count];
                for (int i = 0; i < Members.Count; i++)
                {
                    Members[i].Name = memberNames[i];
                    MemberNames[i] = dictionary.Add(Members[i].Name);
                    MemberNamespaces[i] = Namespace;
                }
                EnsureMethodsImported();
            }
 
            private void EnsureIsReferenceImported(Type type)
            {
                bool isReference = false;
                bool hasDataContractAttribute = TryGetDCAttribute(type, out DataContractAttribute? dataContractAttribute);
 
                if (BaseClassContract != null)
                {
                    if (hasDataContractAttribute && dataContractAttribute!.IsReferenceSetExplicitly)
                    {
                        bool baseIsReference = BaseClassContract.IsReference;
                        if ((baseIsReference && !dataContractAttribute.IsReference) ||
                            (!baseIsReference && dataContractAttribute.IsReference))
                        {
                            DataContract.ThrowInvalidDataContractException(
                                    SR.Format(SR.InconsistentIsReference,
                                        DataContract.GetClrTypeFullName(type),
                                        dataContractAttribute.IsReference,
                                        DataContract.GetClrTypeFullName(BaseClassContract.UnderlyingType),
                                        BaseClassContract.IsReference),
                                    type);
                        }
                        else
                        {
                            isReference = dataContractAttribute.IsReference;
                        }
                    }
                    else
                    {
                        isReference = BaseClassContract.IsReference;
                    }
                }
                else if (hasDataContractAttribute)
                {
                    if (dataContractAttribute!.IsReference)
                        isReference = dataContractAttribute.IsReference;
                }
 
                if (isReference && type.IsValueType)
                {
                    DataContract.ThrowInvalidDataContractException(
                            SR.Format(SR.ValueTypeCannotHaveIsReference,
                                DataContract.GetClrTypeFullName(type),
                                true,
                                false),
                            type);
                    return;
                }
 
                IsReference = isReference;
            }
 
            [MemberNotNull(nameof(_members))]
            [MemberNotNull(nameof(Members))]
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            private void ImportDataMembers()
            {
                Type type = UnderlyingType;
                EnsureIsReferenceImported(type);
                List<DataMember> tempMembers = new List<DataMember>();
                Dictionary<string, DataMember> memberNamesTable = new Dictionary<string, DataMember>();
 
                MemberInfo[] memberInfos;
 
                if (_isNonAttributedType)
                {
                    memberInfos = type.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
                }
                else
                {
                    memberInfos = type.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                }
 
                for (int i = 0; i < memberInfos.Length; i++)
                {
                    MemberInfo member = memberInfos[i];
                    if (HasDataContract)
                    {
                        object[] memberAttributes = member.GetCustomAttributes(typeof(DataMemberAttribute), false).ToArray();
                        if (memberAttributes != null && memberAttributes.Length > 0)
                        {
                            if (memberAttributes.Length > 1)
                                ThrowInvalidDataContractException(SR.Format(SR.TooManyDataMembers, DataContract.GetClrTypeFullName(member.DeclaringType!), member.Name));
 
                            DataMember memberContract = new DataMember(member);
 
                            if (member is PropertyInfo property)
                            {
                                MethodInfo? getMethod = property.GetMethod;
                                if (getMethod != null && IsMethodOverriding(getMethod))
                                    continue;
                                MethodInfo? setMethod = property.SetMethod;
                                if (setMethod != null && IsMethodOverriding(setMethod))
                                    continue;
                                if (getMethod == null)
                                    ThrowInvalidDataContractException(SR.Format(SR.NoGetMethodForProperty, property.DeclaringType, property.Name));
                                if (setMethod == null)
                                {
                                    if (!SetIfGetOnlyCollection(memberContract, skipIfReadOnlyContract: false))
                                    {
                                        _serializationExceptionMessage = SR.Format(SR.NoSetMethodForProperty, property.DeclaringType, property.Name);
                                    }
                                }
                                if (getMethod.GetParameters().Length > 0)
                                    ThrowInvalidDataContractException(SR.Format(SR.IndexedPropertyCannotBeSerialized, property.DeclaringType, property.Name));
                            }
                            else if (!(member is FieldInfo))
                                ThrowInvalidDataContractException(SR.Format(SR.InvalidMember, DataContract.GetClrTypeFullName(type), member.Name));
 
                            DataMemberAttribute memberAttribute = (DataMemberAttribute)memberAttributes[0];
                            if (memberAttribute.IsNameSetExplicitly)
                            {
                                if (string.IsNullOrEmpty(memberAttribute.Name))
                                    ThrowInvalidDataContractException(SR.Format(SR.InvalidDataMemberName, member.Name, DataContract.GetClrTypeFullName(type)));
                                memberContract.Name = memberAttribute.Name;
                            }
                            else
                                memberContract.Name = member.Name;
 
                            memberContract.Name = DataContract.EncodeLocalName(memberContract.Name);
                            memberContract.IsNullable = DataContract.IsTypeNullable(memberContract.MemberType);
                            memberContract.IsRequired = memberAttribute.IsRequired;
                            if (memberAttribute.IsRequired && IsReference)
                            {
                                ThrowInvalidDataContractException(
                                    SR.Format(SR.IsRequiredDataMemberOnIsReferenceDataContractType,
                                    DataContract.GetClrTypeFullName(member.DeclaringType!),
                                    member.Name, true), type);
                            }
                            memberContract.EmitDefaultValue = memberAttribute.EmitDefaultValue;
                            memberContract.Order = memberAttribute.Order;
                            CheckAndAddMember(tempMembers, memberContract, memberNamesTable);
                        }
                    }
                    else if (_isNonAttributedType)
                    {
                        FieldInfo? field = member as FieldInfo;
                        PropertyInfo? property = member as PropertyInfo;
                        if ((field == null && property == null) || (field != null && field.IsInitOnly))
                            continue;
 
                        object[] memberAttributes = member.GetCustomAttributes(typeof(IgnoreDataMemberAttribute), false).ToArray();
                        if (memberAttributes != null && memberAttributes.Length > 0)
                        {
                            if (memberAttributes.Length > 1)
                                ThrowInvalidDataContractException(SR.Format(SR.TooManyIgnoreDataMemberAttributes, DataContract.GetClrTypeFullName(member.DeclaringType!), member.Name));
                            else
                                continue;
                        }
                        DataMember memberContract = new DataMember(member);
                        if (property != null)
                        {
                            MethodInfo? getMethod = property.GetGetMethod();
                            if (getMethod == null || IsMethodOverriding(getMethod) || getMethod.GetParameters().Length > 0)
                                continue;
 
                            MethodInfo? setMethod = property.SetMethod;
                            if (setMethod == null)
                            {
                                if (!SetIfGetOnlyCollection(memberContract, skipIfReadOnlyContract: true))
                                    continue;
                            }
                            else
                            {
                                if (!setMethod.IsPublic || IsMethodOverriding(setMethod))
                                    continue;
                            }
 
                            //skip ExtensionData member of type ExtensionDataObject if IExtensibleDataObject is implemented in non-attributed type
                            if (_hasExtensionData && memberContract.MemberType == Globals.TypeOfExtensionDataObject
                                && member.Name == Globals.ExtensionDataObjectPropertyName)
                                continue;
                        }
 
                        memberContract.Name = DataContract.EncodeLocalName(member.Name);
                        memberContract.IsNullable = DataContract.IsTypeNullable(memberContract.MemberType);
                        CheckAndAddMember(tempMembers, memberContract, memberNamesTable);
                    }
                    else
                    {
                        FieldInfo? field = member as FieldInfo;
 
#pragma warning disable SYSLIB0050 // Field.IsNotSerialized is obsolete
                        if (field != null && !field.IsNotSerialized)
#pragma warning restore SYSLIB0050
                        {
                            DataMember memberContract = new DataMember(member);
 
                            memberContract.Name = DataContract.EncodeLocalName(member.Name);
                            object[] optionalFields = field!.GetCustomAttributes(Globals.TypeOfOptionalFieldAttribute, false);
                            if (optionalFields == null || optionalFields.Length == 0)
                            {
                                if (IsReference)
                                {
                                    ThrowInvalidDataContractException(
                                        SR.Format(SR.NonOptionalFieldMemberOnIsReferenceSerializableType,
                                        DataContract.GetClrTypeFullName(member.DeclaringType!),
                                        member.Name, true), type);
                                }
                                memberContract.IsRequired = true;
                            }
                            memberContract.IsNullable = DataContract.IsTypeNullable(memberContract.MemberType);
                            CheckAndAddMember(tempMembers, memberContract, memberNamesTable);
                        }
                    }
                }
                if (tempMembers.Count > 1)
                    tempMembers.Sort(DataMemberComparer.Singleton);
 
                SetIfMembersHaveConflict(tempMembers);
 
                Interlocked.MemoryBarrier();
                _members = tempMembers;
                Debug.Assert(Members != null);
            }
 
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            private static bool SetIfGetOnlyCollection(DataMember memberContract, bool skipIfReadOnlyContract)
            {
                //OK to call IsCollection here since the use of surrogated collection types is not supported in get-only scenarios
                if (CollectionDataContract.IsCollection(memberContract.MemberType, false /*isConstructorRequired*/, skipIfReadOnlyContract) && !memberContract.MemberType.IsValueType)
                {
                    memberContract.IsGetOnlyCollection = true;
                    return true;
                }
                return false;
            }
 
            private void SetIfMembersHaveConflict(List<DataMember> members)
            {
                if (BaseClassContract == null)
                    return;
 
                int baseTypeIndex = 0;
                List<Member> membersInHierarchy = new List<Member>();
                foreach (DataMember member in members)
                {
                    membersInHierarchy.Add(new Member(member, XmlName!.Namespace, baseTypeIndex));
                }
                ClassDataContract? currContract = BaseClassContract;
                while (currContract != null)
                {
                    baseTypeIndex++;
 
                    foreach (DataMember member in currContract.Members!)
                    {
                        membersInHierarchy.Add(new Member(member, currContract.XmlName!.Namespace, baseTypeIndex));
                    }
                    currContract = currContract.BaseClassContract;
                }
 
                IComparer<Member> comparer = DataMemberConflictComparer.Singleton;
                membersInHierarchy.Sort(comparer);
 
                for (int i = 0; i < membersInHierarchy.Count - 1; i++)
                {
                    int startIndex = i;
                    int endIndex = i;
                    bool hasConflictingType = false;
                    while (endIndex < membersInHierarchy.Count - 1
                        && membersInHierarchy[endIndex]._member.Name == membersInHierarchy[endIndex + 1]._member.Name
                        && membersInHierarchy[endIndex]._ns == membersInHierarchy[endIndex + 1]._ns)
                    {
                        membersInHierarchy[endIndex]._member.ConflictingMember = membersInHierarchy[endIndex + 1]._member;
                        if (!hasConflictingType)
                        {
                            if (membersInHierarchy[endIndex + 1]._member.HasConflictingNameAndType)
                            {
                                hasConflictingType = true;
                            }
                            else
                            {
                                hasConflictingType = (membersInHierarchy[endIndex]._member.MemberType != membersInHierarchy[endIndex + 1]._member.MemberType);
                            }
                        }
                        endIndex++;
                    }
 
                    if (hasConflictingType)
                    {
                        for (int j = startIndex; j <= endIndex; j++)
                        {
                            membersInHierarchy[j]._member.HasConflictingNameAndType = true;
                        }
                    }
 
                    i = endIndex + 1;
                }
            }
 
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            private XmlQualifiedName GetXmlNameAndSetHasDataContract(Type type)
            {
                return DataContract.GetXmlName(type, out _hasDataContract);
            }
 
            /// <SecurityNote>
            /// RequiresReview - marked SRR because callers may need to depend on isNonAttributedType for a security decision
            ///            isNonAttributedType must be calculated correctly
            ///            SetIsNonAttributedType should not be called before GetXmlNameAndSetHasDataContract since it
            ///            is dependent on the correct calculation of hasDataContract
            /// Safe - does not let caller influence isNonAttributedType calculation; no harm in leaking value
            /// </SecurityNote>
            private void SetIsNonAttributedType(
                [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.Interfaces)]
                Type type)
            {
#pragma warning disable SYSLIB0050 // Type.IsSerializable is obsolete
                _isNonAttributedType = !type.IsSerializable && !_hasDataContract && IsNonAttributedTypeValidForSerialization(type);
#pragma warning restore SYSLIB0050
            }
 
            private static bool IsMethodOverriding(MethodInfo method)
            {
                return method.IsVirtual && ((method.Attributes & MethodAttributes.NewSlot) == 0);
            }
 
            internal void EnsureMethodsImported()
            {
                if (!_isMethodChecked && UnderlyingType != null)
                {
                    lock (this)
                    {
                        if (!_isMethodChecked)
                        {
                            Type type = UnderlyingType;
                            MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                            for (int i = 0; i < methods.Length; i++)
                            {
                                MethodInfo method = methods[i];
                                Type? prevAttributeType = null;
                                ParameterInfo[] parameters = method.GetParameters();
                                if (HasExtensionData && IsValidExtensionDataSetMethod(method, parameters))
                                {
                                    if (method.Name == Globals.ExtensionDataSetExplicitMethod || !method.IsPublic)
                                        _extensionDataSetMethod = XmlFormatGeneratorStatics.ExtensionDataSetExplicitMethodInfo;
                                    else
                                        _extensionDataSetMethod = method;
                                }
                                if (IsValidCallback(method, parameters, Globals.TypeOfOnSerializingAttribute, _onSerializing, ref prevAttributeType))
                                    _onSerializing = method;
                                if (IsValidCallback(method, parameters, Globals.TypeOfOnSerializedAttribute, _onSerialized, ref prevAttributeType))
                                    _onSerialized = method;
                                if (IsValidCallback(method, parameters, Globals.TypeOfOnDeserializingAttribute, _onDeserializing, ref prevAttributeType))
                                    _onDeserializing = method;
                                if (IsValidCallback(method, parameters, Globals.TypeOfOnDeserializedAttribute, _onDeserialized, ref prevAttributeType))
                                    _onDeserialized = method;
                            }
                            Interlocked.MemoryBarrier();
                            _isMethodChecked = true;
                        }
                    }
                }
            }
 
            private bool IsValidExtensionDataSetMethod(MethodInfo method, ParameterInfo[] parameters)
            {
                if (method.Name == Globals.ExtensionDataSetExplicitMethod || method.Name == Globals.ExtensionDataSetMethod)
                {
                    Debug.Assert(method.DeclaringType != null);
 
                    if (_extensionDataSetMethod != null)
                        ThrowInvalidDataContractException(SR.Format(SR.DuplicateExtensionDataSetMethod, method, _extensionDataSetMethod, DataContract.GetClrTypeFullName(method.DeclaringType)));
                    if (method.ReturnType != Globals.TypeOfVoid)
                        DataContract.ThrowInvalidDataContractException(SR.Format(SR.ExtensionDataSetMustReturnVoid, DataContract.GetClrTypeFullName(method.DeclaringType), method), method.DeclaringType);
                    if (parameters == null || parameters.Length != 1 || parameters[0].ParameterType != Globals.TypeOfExtensionDataObject)
                        DataContract.ThrowInvalidDataContractException(SR.Format(SR.ExtensionDataSetParameterInvalid, DataContract.GetClrTypeFullName(method.DeclaringType), method, Globals.TypeOfExtensionDataObject), method.DeclaringType);
                    return true;
                }
                return false;
            }
 
            private static bool IsValidCallback(MethodInfo method, ParameterInfo[] parameters, Type attributeType, MethodInfo? currentCallback, ref Type? prevAttributeType)
            {
                if (method.IsDefined(attributeType, false))
                {
                    Debug.Assert(method.DeclaringType != null);
 
                    if (currentCallback != null)
                        DataContract.ThrowInvalidDataContractException(SR.Format(SR.DuplicateCallback, method, currentCallback, DataContract.GetClrTypeFullName(method.DeclaringType), attributeType), method.DeclaringType);
                    else if (prevAttributeType != null)
                        DataContract.ThrowInvalidDataContractException(SR.Format(SR.DuplicateAttribute, prevAttributeType, attributeType, DataContract.GetClrTypeFullName(method.DeclaringType), method), method.DeclaringType);
                    else if (method.IsVirtual)
                        DataContract.ThrowInvalidDataContractException(SR.Format(SR.CallbacksCannotBeVirtualMethods, method, DataContract.GetClrTypeFullName(method.DeclaringType), attributeType), method.DeclaringType);
                    else
                    {
                        if (method.ReturnType != Globals.TypeOfVoid)
                            DataContract.ThrowInvalidDataContractException(SR.Format(SR.CallbackMustReturnVoid, DataContract.GetClrTypeFullName(method.DeclaringType), method), method.DeclaringType);
                        if (parameters == null || parameters.Length != 1 || parameters[0].ParameterType != Globals.TypeOfStreamingContext)
                            DataContract.ThrowInvalidDataContractException(SR.Format(SR.CallbackParameterInvalid, DataContract.GetClrTypeFullName(method.DeclaringType), method, Globals.TypeOfStreamingContext), method.DeclaringType);
 
                        prevAttributeType = attributeType;
                    }
                    return true;
                }
                return false;
            }
 
            internal ClassDataContract? BaseClassContract
            {
                get => _baseContract;
                set
                {
                    _baseContract = value;
                    if (_baseContract != null && IsValueType)
                        ThrowInvalidDataContractException(SR.Format(SR.ValueTypeCannotHaveBaseType, XmlName!.Name, XmlName.Namespace, _baseContract.XmlName!.Name, _baseContract.XmlName.Namespace));
                }
            }
 
            internal List<DataMember>? Members
            {
                get => _members;
                set => _members = value;
            }
 
            internal MethodInfo? OnSerializing
            {
                get
                {
                    EnsureMethodsImported();
                    return _onSerializing;
                }
            }
 
            internal MethodInfo? OnSerialized
            {
                get
                {
                    EnsureMethodsImported();
                    return _onSerialized;
                }
            }
 
            internal MethodInfo? OnDeserializing
            {
                get
                {
                    EnsureMethodsImported();
                    return _onDeserializing;
                }
            }
 
            internal MethodInfo? OnDeserialized
            {
                get
                {
                    EnsureMethodsImported();
                    return _onDeserialized;
                }
            }
 
            internal MethodInfo? ExtensionDataSetMethod
            {
                get
                {
                    EnsureMethodsImported();
                    return _extensionDataSetMethod;
                }
            }
 
            internal override DataContractDictionary? KnownDataContracts
            {
                [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
                [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
                get
                {
                    if (!_isKnownTypeAttributeChecked && UnderlyingType != null)
                    {
                        lock (this)
                        {
                            if (!_isKnownTypeAttributeChecked)
                            {
                                _knownDataContracts = DataContract.ImportKnownTypeAttributes(UnderlyingType);
                                Interlocked.MemoryBarrier();
                                _isKnownTypeAttributeChecked = true;
                            }
                            _knownDataContracts ??= new DataContractDictionary();
                        }
                    }
                    return _knownDataContracts;
                }
 
                set { _knownDataContracts = value; }
            }
 
            internal string? SerializationExceptionMessage => _serializationExceptionMessage;
 
            internal string? DeserializationExceptionMessage => (_serializationExceptionMessage == null) ? null : SR.Format(SR.ReadOnlyClassDeserialization, _serializationExceptionMessage);
 
            internal override bool IsISerializable { get; set; }
 
            internal bool HasDataContract => _hasDataContract;
 
            internal bool HasExtensionData => _hasExtensionData;
 
            internal bool IsNonAttributedType => _isNonAttributedType;
 
            internal ConstructorInfo? GetISerializableConstructor()
            {
                if (!IsISerializable)
                    return null;
 
                ConstructorInfo? ctor = UnderlyingType.GetConstructor(Globals.ScanAllMembers, SerInfoCtorArgs);
                if (ctor == null)
                    throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.SerializationInfo_ConstructorNotFound, DataContract.GetClrTypeFullName(UnderlyingType)));
 
                return ctor;
            }
 
            internal ConstructorInfo? GetNonAttributedTypeConstructor()
            {
                if (!IsNonAttributedType)
                    return null;
 
                Type type = UnderlyingType;
 
                if (type.IsValueType)
                    return null;
 
                ConstructorInfo? ctor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, Type.EmptyTypes);
                if (ctor == null)
                    throw new InvalidDataContractException(SR.Format(SR.NonAttributedSerializableTypesMustHaveDefaultConstructor, DataContract.GetClrTypeFullName(type)));
 
                return ctor;
            }
 
            internal XmlFormatClassWriterDelegate? XmlFormatWriterDelegate { get; set; }
 
            internal XmlFormatClassReaderDelegate? XmlFormatReaderDelegate { get; set; }
 
            internal XmlDictionaryString?[]? ChildElementNamespaces { get; set; }
 
            private static Type[] SerInfoCtorArgs => s_serInfoCtorArgs ??= new Type[] { typeof(SerializationInfo), typeof(StreamingContext) };
 
            internal readonly struct Member
            {
                internal Member(DataMember member, string ns, int baseTypeIndex)
                {
                    _member = member;
                    _ns = ns;
                    _baseTypeIndex = baseTypeIndex;
                }
                internal readonly DataMember _member;
                internal readonly string _ns;
                internal readonly int _baseTypeIndex;
            }
 
            internal sealed class DataMemberConflictComparer : IComparer<Member>
            {
                public int Compare(Member x, Member y)
                {
                    int nsCompare = string.CompareOrdinal(x._ns, y._ns);
                    if (nsCompare != 0)
                        return nsCompare;
 
                    int nameCompare = string.CompareOrdinal(x._member.Name, y._member.Name);
                    if (nameCompare != 0)
                        return nameCompare;
 
                    return x._baseTypeIndex - y._baseTypeIndex;
                }
 
                internal static readonly DataMemberConflictComparer Singleton = new DataMemberConflictComparer();
            }
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal override DataContract BindGenericParameters(DataContract[] paramContracts, Dictionary<DataContract, DataContract>? boundContracts = null)
        {
            Type type = UnderlyingType;
            if (!type.IsGenericType || !type.ContainsGenericParameters)
                return this;
 
            lock (this)
            {
                if (boundContracts != null && boundContracts.TryGetValue(this, out DataContract? boundContract))
                    return boundContract;
 
                XmlQualifiedName xmlName;
                object[] genericParams;
                Type boundType;
                if (type.IsGenericTypeDefinition)
                {
                    xmlName = XmlName;
                    genericParams = paramContracts;
 
                    // This type-binding ('boundType') stuff is new. We did not do this in NetFx. We used to use default contract constructors and let the
                    // underlying type get filled in later. But default constructors for DataContracts runs afoul of requiring an underlying type. Our web of nullable
                    // notations make it hard to get around. But it also allows us to feel good about using .UnderlyingType from matching parameter contracts.
                    Type[] underlyingParamTypes = new Type[paramContracts.Length];
                    for (int i = 0; i < paramContracts.Length; i++)
                        underlyingParamTypes[i] = paramContracts[i].UnderlyingType;
                    boundType = type.MakeGenericType(underlyingParamTypes);
                }
                else
                {
                    //partial Generic: Construct xml name from its open generic type definition
                    xmlName = DataContract.GetXmlName(type.GetGenericTypeDefinition());
                    Type[] paramTypes = type.GetGenericArguments();
                    genericParams = new object[paramTypes.Length];
                    for (int i = 0; i < paramTypes.Length; i++)
                    {
                        Type paramType = paramTypes[i];
                        if (paramType.IsGenericParameter)
                        {
                            genericParams[i] = paramContracts[paramType.GenericParameterPosition];
                            paramTypes[i] = paramContracts[paramType.GenericParameterPosition].UnderlyingType;
                        }
                        else
                        {
                            genericParams[i] = paramType;
                        }
                    }
                    boundType = type.MakeGenericType(paramTypes);
                }
                ClassDataContract boundClassContract = new ClassDataContract(boundType);
                boundContracts ??= new Dictionary<DataContract, DataContract>();
                boundContracts.Add(this, boundClassContract);
                boundClassContract.XmlName = CreateQualifiedName(DataContract.ExpandGenericParameters(XmlConvert.DecodeName(xmlName.Name), new GenericNameProvider(DataContract.GetClrTypeFullName(UnderlyingType), genericParams)), xmlName.Namespace);
                if (BaseClassContract != null)
                    boundClassContract.BaseClassContract = (ClassDataContract)BaseClassContract.BindGenericParameters(paramContracts, boundContracts);
                boundClassContract.IsISerializable = IsISerializable;
                boundClassContract.IsValueType = IsValueType;
                boundClassContract.IsReference = IsReference;
                if (Members != null)
                {
                    boundClassContract.Members = new List<DataMember>(Members.Count);
                    foreach (DataMember member in Members)
                        boundClassContract.Members.Add(member.BindGenericParameters(paramContracts, boundContracts));
                }
                return boundClassContract;
            }
        }
 
        [UnconditionalSuppressMessage("AOT Analysis", "IL3050:RequiresDynamicCode",
            Justification = "All ctor's required to create an instance of this type are marked with RequiresDynamicCode.")]
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "All ctor's required to create an instance of this type are marked with RequiresUnreferencedCode.")]
        internal override bool Equals(object? other, HashSet<DataContractPairKey>? checkedContracts)
        {
            if (IsEqualOrChecked(other, checkedContracts))
                return true;
 
            if (base.Equals(other, checkedContracts))
            {
                if (other is ClassDataContract dataContract)
                {
                    if (IsISerializable)
                    {
                        if (!dataContract.IsISerializable)
                            return false;
                    }
                    else
                    {
                        if (dataContract.IsISerializable)
                            return false;
 
                        if (Members == null)
                        {
                            if (dataContract.Members != null)
                            {
                                // check that all the datamembers in dataContract.Members are optional
                                if (!IsEveryDataMemberOptional(dataContract.Members))
                                    return false;
                            }
                        }
                        else if (dataContract.Members == null)
                        {
                            // check that all the datamembers in Members are optional
                            if (!IsEveryDataMemberOptional(Members))
                                return false;
                        }
                        else
                        {
                            Dictionary<string, DataMember> membersDictionary = new Dictionary<string, DataMember>(Members.Count);
                            List<DataMember> dataContractMembersList = new List<DataMember>();
                            for (int i = 0; i < Members.Count; i++)
                            {
                                membersDictionary.Add(Members[i].Name, Members[i]);
                            }
 
                            for (int i = 0; i < dataContract.Members.Count; i++)
                            {
                                // check that all datamembers common to both datacontracts match
                                if (membersDictionary.TryGetValue(dataContract.Members[i].Name, out DataMember? dataMember))
                                {
                                    if (dataMember.Equals(dataContract.Members[i], checkedContracts))
                                    {
                                        membersDictionary.Remove(dataMember.Name);
                                    }
                                    else
                                    {
                                        return false;
                                    }
                                }
                                // otherwise save the non-matching datamembers for later verification
                                else
                                {
                                    dataContractMembersList.Add(dataContract.Members[i]);
                                }
                            }
 
                            // check that datamembers left over from either datacontract are optional
                            if (!IsEveryDataMemberOptional(membersDictionary.Values))
                                return false;
                            if (!IsEveryDataMemberOptional(dataContractMembersList))
                                return false;
                        }
                    }
 
                    if (BaseClassContract == null)
                        return (dataContract.BaseClassContract == null);
                    else if (dataContract.BaseClassContract == null)
                        return false;
                    else
                        return BaseClassContract.Equals(dataContract.BaseClassContract, checkedContracts);
                }
            }
            return false;
        }
 
        private static bool IsEveryDataMemberOptional(IEnumerable<DataMember> dataMembers)
        {
            return !dataMembers.Any(dm => dm.IsRequired);
        }
 
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
 
        internal sealed class DataMemberComparer : IComparer<DataMember>
        {
            public int Compare(DataMember? x, DataMember? y)
            {
                if (x == null && y == null)
                    return 0;
                if (x == null || y == null)
                    return -1;
 
                int orderCompare = (int)(x.Order - y.Order);
                if (orderCompare != 0)
                    return orderCompare;
 
                return string.CompareOrdinal(x.Name, y.Name);
            }
 
            internal static readonly DataMemberComparer Singleton = new DataMemberComparer();
        }
 
        /// <summary>
        ///  Get object type for Xml/JsonFormatReaderGenerator
        /// </summary>
        internal Type ObjectType
        {
            get
            {
                Type type = UnderlyingType;
                if (type.IsValueType && !IsNonAttributedType)
                {
                    type = Globals.TypeOfValueType;
                }
                return type;
            }
        }
    }
}