File: System\Runtime\Serialization\CollectionDataContract.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;
using System.Collections.Generic;
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
{
    internal interface IKeyValue
    {
        object? Key { get; set; }
        object? Value { get; set; }
    }
 
    [DataContract(Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
    internal struct KeyValue<K, V> : IKeyValue
    {
        internal KeyValue(K key, V value)
        {
            Key = key;
            Value = value;
        }
 
        [DataMember(IsRequired = true)]
        public K Key { get; set; }
 
        [DataMember(IsRequired = true)]
        public V Value { get; set; }
 
        object? IKeyValue.Key
        {
            get => this.Key;
            set => this.Key = (K)value!;
        }
 
        object? IKeyValue.Value
        {
            get => this.Value;
            set => this.Value = (V)value!;
        }
    }
 
    internal enum CollectionKind : byte
    {
        None,
        GenericDictionary,
        Dictionary,
        GenericList,
        GenericCollection,
        List,
        GenericEnumerable,
        Collection,
        Enumerable,
        Array,
    }
}
 
namespace System.Runtime.Serialization.DataContracts
{
    internal sealed class CollectionDataContract : DataContract
    {
        internal const string ContractTypeString = nameof(CollectionDataContract);
        public override string? ContractType => ContractTypeString;
 
        private XmlDictionaryString _collectionItemName;
 
        private XmlDictionaryString? _childElementNamespace;
 
        private DataContract? _itemContract;
 
        private CollectionDataContractCriticalHelper _helper;
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal CollectionDataContract(Type type) : base(new CollectionDataContractCriticalHelper(type))
        {
            InitCollectionDataContract(this);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal CollectionDataContract(Type type, DataContract itemContract) : base(new CollectionDataContractCriticalHelper(type, itemContract))
        {
            InitCollectionDataContract(this);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal CollectionDataContract(Type type, CollectionKind kind) : base(new CollectionDataContractCriticalHelper(type, kind))
        {
            InitCollectionDataContract(this);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, string? serializationExceptionMessage, string? deserializationExceptionMessage)
                    : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, serializationExceptionMessage, deserializationExceptionMessage))
        {
            InitCollectionDataContract(GetSharedTypeContract(type));
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo? addMethod, ConstructorInfo? constructor)
                    : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, addMethod, constructor))
        {
            InitCollectionDataContract(GetSharedTypeContract(type));
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo? addMethod, ConstructorInfo? constructor, bool isConstructorCheckRequired)
                    : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, addMethod, constructor, isConstructorCheckRequired))
        {
            InitCollectionDataContract(GetSharedTypeContract(type));
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private CollectionDataContract(Type type, string invalidCollectionInSharedContractMessage) : base(new CollectionDataContractCriticalHelper(type, invalidCollectionInSharedContractMessage))
        {
            InitCollectionDataContract(GetSharedTypeContract(type));
        }
 
        [MemberNotNull(nameof(_helper))]
        [MemberNotNull(nameof(_collectionItemName))]
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private void InitCollectionDataContract(DataContract? sharedTypeContract)
        {
            _helper = (base.Helper as CollectionDataContractCriticalHelper)!;
            _collectionItemName = _helper.CollectionItemName;
            if (_helper.Kind == CollectionKind.Dictionary || _helper.Kind == CollectionKind.GenericDictionary)
            {
                _itemContract = _helper.ItemContract;
            }
            _helper.SharedTypeContract = sharedTypeContract;
        }
 
        private static Type[] KnownInterfaces => CollectionDataContractCriticalHelper.KnownInterfaces;
 
        internal CollectionKind Kind => _helper.Kind;
 
        internal Type ItemType => _helper.ItemType;
 
        internal DataContract ItemContract
        {
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            get => _itemContract ?? _helper.ItemContract;
 
            set
            {
                _itemContract = value;
                _helper.ItemContract = value;
            }
        }
 
        internal DataContract? SharedTypeContract => _helper.SharedTypeContract;
 
        internal string ItemName
        {
            get => _helper.ItemName;
            set => _helper.ItemName = value;
        }
 
        internal XmlDictionaryString CollectionItemName => _collectionItemName;
 
        internal string? KeyName
        {
            get => _helper.KeyName;
            set => _helper.KeyName = value;
        }
 
        internal string? ValueName
        {
            get => _helper.ValueName;
            set => _helper.ValueName = value;
        }
 
        public override bool IsDictionaryLike([NotNullWhen(true)] out string? keyName, [NotNullWhen(true)] out string? valueName, [NotNullWhen(true)] out string? itemName)
        {
            keyName = KeyName;
            valueName = ValueName;
            itemName = ItemName;
            return IsDictionary;
        }
 
        public override DataContract BaseContract
        {
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            get => ItemContract;
        }
 
        internal bool IsDictionary => KeyName != null;
 
        internal XmlDictionaryString? ChildElementNamespace
        {
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            get
            {
                if (_childElementNamespace == null)
                {
                    lock (this)
                    {
                        if (_childElementNamespace == null)
                        {
                            if (_helper.ChildElementNamespace == null && !IsDictionary)
                            {
                                XmlDictionaryString? tempChildElementNamespace = ClassDataContract.GetChildNamespaceToDeclare(this, ItemType, new XmlDictionary());
                                Interlocked.MemoryBarrier();
                                _helper.ChildElementNamespace = tempChildElementNamespace;
                            }
                            _childElementNamespace = _helper.ChildElementNamespace;
                        }
                    }
                }
                return _childElementNamespace;
            }
        }
 
        internal bool IsItemTypeNullable
        {
            get => _helper.IsItemTypeNullable;
            set => _helper.IsItemTypeNullable = value;
        }
 
        internal bool IsConstructorCheckRequired
        {
            get => _helper.IsConstructorCheckRequired;
            set => _helper.IsConstructorCheckRequired = value;
        }
 
        internal MethodInfo? GetEnumeratorMethod => _helper.GetEnumeratorMethod;
 
        internal MethodInfo? AddMethod => _helper.AddMethod;
 
        internal ConstructorInfo? Constructor => _helper.Constructor;
 
        public override DataContractDictionary? KnownDataContracts
        {
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            get => _helper.KnownDataContracts;
            internal set => _helper.KnownDataContracts = value;
        }
 
        internal string? InvalidCollectionInSharedContractMessage => _helper.InvalidCollectionInSharedContractMessage;
 
        internal string? SerializationExceptionMessage => _helper.SerializationExceptionMessage;
 
        internal string? DeserializationExceptionMessage => _helper.DeserializationExceptionMessage;
 
        internal bool IsReadOnlyContract => DeserializationExceptionMessage != null;
 
        private bool ItemNameSetExplicit => _helper.ItemNameSetExplicit;
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private XmlFormatCollectionWriterDelegate CreateXmlFormatWriterDelegate()
        {
            return new XmlFormatWriterGenerator().GenerateCollectionWriter(this);
        }
 
        internal XmlFormatCollectionWriterDelegate XmlFormatWriterDelegate
        {
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            get
            {
                if (_helper.XmlFormatWriterDelegate == null)
                {
                    lock (this)
                    {
                        if (_helper.XmlFormatWriterDelegate == null)
                        {
                            XmlFormatCollectionWriterDelegate tempDelegate = CreateXmlFormatWriterDelegate();
                            Interlocked.MemoryBarrier();
                            _helper.XmlFormatWriterDelegate = tempDelegate;
                        }
                    }
                }
                return _helper.XmlFormatWriterDelegate;
            }
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private XmlFormatCollectionReaderDelegate CreateXmlFormatReaderDelegate()
        {
            return new XmlFormatReaderGenerator().GenerateCollectionReader(this);
        }
 
        internal XmlFormatCollectionReaderDelegate XmlFormatReaderDelegate
        {
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            get
            {
                if (_helper.XmlFormatReaderDelegate == null)
                {
                    lock (this)
                    {
                        if (_helper.XmlFormatReaderDelegate == null)
                        {
                            if (IsReadOnlyContract)
                            {
                                ThrowInvalidDataContractException(_helper.DeserializationExceptionMessage, type: null);
                            }
 
                            XmlFormatCollectionReaderDelegate tempDelegate = CreateXmlFormatReaderDelegate();
                            Interlocked.MemoryBarrier();
                            _helper.XmlFormatReaderDelegate = tempDelegate;
                        }
                    }
                }
                return _helper.XmlFormatReaderDelegate;
            }
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private XmlFormatGetOnlyCollectionReaderDelegate CreateXmlFormatGetOnlyCollectionReaderDelegate()
        {
            return new XmlFormatReaderGenerator().GenerateGetOnlyCollectionReader(this);
        }
 
 
        internal XmlFormatGetOnlyCollectionReaderDelegate XmlFormatGetOnlyCollectionReaderDelegate
        {
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            get
            {
                if (_helper.XmlFormatGetOnlyCollectionReaderDelegate == null)
                {
                    lock (this)
                    {
                        if (_helper.XmlFormatGetOnlyCollectionReaderDelegate == null)
                        {
                            if (UnderlyingType.IsInterface && (Kind == CollectionKind.Enumerable || Kind == CollectionKind.Collection || Kind == CollectionKind.GenericEnumerable))
                            {
                                throw new InvalidDataContractException(SR.Format(SR.GetOnlyCollectionMustHaveAddMethod, GetClrTypeFullName(UnderlyingType)));
                            }
 
                            if (IsReadOnlyContract)
                            {
                                ThrowInvalidDataContractException(_helper.DeserializationExceptionMessage, type: null);
                            }
 
                            if (Kind != CollectionKind.Array && AddMethod == null)
                            {
                                throw new InvalidDataContractException(SR.Format(SR.GetOnlyCollectionMustHaveAddMethod, GetClrTypeFullName(UnderlyingType)));
                            }
 
                            XmlFormatGetOnlyCollectionReaderDelegate tempDelegate = CreateXmlFormatGetOnlyCollectionReaderDelegate();
                            Interlocked.MemoryBarrier();
                            _helper.XmlFormatGetOnlyCollectionReaderDelegate = tempDelegate;
                        }
                    }
                }
                return _helper.XmlFormatGetOnlyCollectionReaderDelegate;
            }
            set
            {
            }
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        internal void IncrementCollectionCount(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context)
        {
            _helper.IncrementCollectionCount(xmlWriter, obj, context);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        internal IEnumerator GetEnumeratorForCollection(object obj)
        {
            return _helper.GetEnumeratorForCollection(obj);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal Type GetCollectionElementType()
        {
            return _helper.GetCollectionElementType();
        }
 
        private sealed class CollectionDataContractCriticalHelper : DataContract.DataContractCriticalHelper
        {
            private static Type[]? s_knownInterfaces;
 
            private Type _itemType = null!; // _itemType is always set except for the "invalid" CollectionDataContract
            private bool _isItemTypeNullable;
            private CollectionKind _kind;
            private readonly MethodInfo? _getEnumeratorMethod;
            private readonly MethodInfo? _addMethod;
            private readonly ConstructorInfo? _constructor;
            private readonly string? _serializationExceptionMessage;
            private readonly string? _deserializationExceptionMessage;
            private DataContract? _itemContract;
            private DataContract? _sharedTypeContract;
            private DataContractDictionary? _knownDataContracts;
            private bool _isKnownTypeAttributeChecked;
            private string _itemName = null!; // _itemName is always set except for the "invalid" CollectionDataContract
            private bool _itemNameSetExplicit;
            private XmlDictionaryString _collectionItemName = null!; // _itemName is always set except for the "invalid" CollectionDataContract
            private string? _keyName;
            private string? _valueName;
            private XmlDictionaryString? _childElementNamespace;
            private readonly string? _invalidCollectionInSharedContractMessage;
            private XmlFormatCollectionReaderDelegate? _xmlFormatReaderDelegate;
            private XmlFormatGetOnlyCollectionReaderDelegate? _xmlFormatGetOnlyCollectionReaderDelegate;
            private XmlFormatCollectionWriterDelegate? _xmlFormatWriterDelegate;
            private bool _isConstructorCheckRequired;
 
            internal static Type[] KnownInterfaces =>
                // Listed in priority order
                s_knownInterfaces ??= new Type[]
                {
                    Globals.TypeOfIDictionaryGeneric,
                    Globals.TypeOfIDictionary,
                    Globals.TypeOfIListGeneric,
                    Globals.TypeOfICollectionGeneric,
                    Globals.TypeOfIList,
                    Globals.TypeOfIEnumerableGeneric,
                    Globals.TypeOfICollection,
                    Globals.TypeOfIEnumerable
                };
 
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            private void Init(CollectionKind kind, Type? itemType, CollectionDataContractAttribute? collectionContractAttribute)
            {
                _kind = kind;
                if (itemType != null)
                {
                    _itemType = itemType;
                    _isItemTypeNullable = DataContract.IsTypeNullable(itemType);
 
                    bool isDictionary = (kind == CollectionKind.Dictionary || kind == CollectionKind.GenericDictionary);
                    string? itemName = null, keyName = null, valueName = null;
                    if (collectionContractAttribute != null)
                    {
                        if (collectionContractAttribute.IsItemNameSetExplicitly)
                        {
                            if (string.IsNullOrEmpty(collectionContractAttribute.ItemName))
                                throw new InvalidDataContractException(SR.Format(SR.InvalidCollectionContractItemName, DataContract.GetClrTypeFullName(UnderlyingType)));
                            itemName = DataContract.EncodeLocalName(collectionContractAttribute.ItemName);
                            _itemNameSetExplicit = true;
                        }
                        if (collectionContractAttribute.IsKeyNameSetExplicitly)
                        {
                            if (string.IsNullOrEmpty(collectionContractAttribute.KeyName))
                                throw new InvalidDataContractException(SR.Format(SR.InvalidCollectionContractKeyName, DataContract.GetClrTypeFullName(UnderlyingType)));
                            if (!isDictionary)
                                throw new InvalidDataContractException(SR.Format(SR.InvalidCollectionContractKeyNoDictionary, DataContract.GetClrTypeFullName(UnderlyingType), collectionContractAttribute.KeyName));
                            keyName = DataContract.EncodeLocalName(collectionContractAttribute.KeyName);
                        }
                        if (collectionContractAttribute.IsValueNameSetExplicitly)
                        {
                            if (string.IsNullOrEmpty(collectionContractAttribute.ValueName))
                                throw new InvalidDataContractException(SR.Format(SR.InvalidCollectionContractValueName, DataContract.GetClrTypeFullName(UnderlyingType)));
                            if (!isDictionary)
                                throw new InvalidDataContractException(SR.Format(SR.InvalidCollectionContractValueNoDictionary, DataContract.GetClrTypeFullName(UnderlyingType), collectionContractAttribute.ValueName));
                            valueName = DataContract.EncodeLocalName(collectionContractAttribute.ValueName);
                        }
                    }
 
                    XmlDictionary dictionary = isDictionary ? new XmlDictionary(5) : new XmlDictionary(3);
                    Name = dictionary.Add(XmlName.Name);
                    Namespace = dictionary.Add(XmlName.Namespace);
                    _itemName = itemName ?? DataContract.GetXmlName(DataContract.UnwrapNullableType(itemType)).Name;
                    _collectionItemName = dictionary.Add(_itemName);
                    if (isDictionary)
                    {
                        _keyName = keyName ?? Globals.KeyLocalName;
                        _valueName = valueName ?? Globals.ValueLocalName;
                    }
                }
                if (collectionContractAttribute != null)
                {
                    IsReference = collectionContractAttribute.IsReference;
                }
            }
 
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            internal CollectionDataContractCriticalHelper(
                [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)]
                Type type) : base(type)
            {
                if (type == Globals.TypeOfArray)
                    type = Globals.TypeOfObjectArray;
                if (type.GetArrayRank() > 1)
                    throw new NotSupportedException(SR.SupportForMultidimensionalArraysNotPresent);
                XmlName = DataContract.GetXmlName(type);
                Init(CollectionKind.Array, type.GetElementType(), null);
            }
 
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            internal CollectionDataContractCriticalHelper(
                [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)]
                Type type,
                CollectionKind kind) : base(type)
            {
                XmlName = DataContract.GetXmlName(type);
                Init(kind, type.GetElementType(), null);
            }
 
            // array
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            internal CollectionDataContractCriticalHelper(
                [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)]
                Type type,
                DataContract itemContract) : base(type)
            {
                if (type.GetArrayRank() > 1)
                    throw new NotSupportedException(SR.SupportForMultidimensionalArraysNotPresent);
                XmlName = CreateQualifiedName(Globals.ArrayPrefix + itemContract.XmlName.Name, itemContract.XmlName.Namespace);
                _itemContract = itemContract;
                Init(CollectionKind.Array, type.GetElementType(), null);
            }
 
            // read-only collection
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            internal CollectionDataContractCriticalHelper(
                [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)]
                Type type,
                CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, string? serializationExceptionMessage, string? deserializationExceptionMessage)
                : base(type)
            {
                if (getEnumeratorMethod == null)
                    throw new InvalidDataContractException(SR.Format(SR.CollectionMustHaveGetEnumeratorMethod, GetClrTypeFullName(type)));
                if (itemType == null)
                    throw new InvalidDataContractException(SR.Format(SR.CollectionMustHaveItemType, GetClrTypeFullName(type)));
 
                CollectionDataContractAttribute? collectionContractAttribute;
                XmlName = DataContract.GetCollectionXmlName(type, itemType, out collectionContractAttribute);
 
                Init(kind, itemType, collectionContractAttribute);
                _getEnumeratorMethod = getEnumeratorMethod;
                _serializationExceptionMessage = serializationExceptionMessage;
                _deserializationExceptionMessage = deserializationExceptionMessage;
            }
 
            // collection
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            internal CollectionDataContractCriticalHelper(
                [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)]
                Type type,
                CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo? addMethod, ConstructorInfo? constructor)
                : this(type, kind, itemType, getEnumeratorMethod, (string?)null, (string?)null)
            {
                if (addMethod == null && !type.IsInterface)
                    throw new InvalidDataContractException(SR.Format(SR.CollectionMustHaveAddMethod, DataContract.GetClrTypeFullName(type)));
 
                _addMethod = addMethod;
                _constructor = constructor;
            }
 
            // collection
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            internal CollectionDataContractCriticalHelper(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo? addMethod, ConstructorInfo? constructor, bool isConstructorCheckRequired)
                : this(type, kind, itemType, getEnumeratorMethod, addMethod, constructor)
            {
                _isConstructorCheckRequired = isConstructorCheckRequired;
            }
 
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            internal CollectionDataContractCriticalHelper(
                [DynamicallyAccessedMembers(ClassDataContract.DataContractPreserveMemberTypes)]
                Type type,
                string invalidCollectionInSharedContractMessage) : base(type)
            {
                Init(CollectionKind.Collection, null /*itemType*/, null);
                _invalidCollectionInSharedContractMessage = invalidCollectionInSharedContractMessage;
            }
 
            internal CollectionKind Kind => _kind;
 
            internal Type ItemType => _itemType;
 
            internal DataContract ItemContract
            {
                [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
                [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
                get
                {
                    if (_itemContract == null)
                    {
                        if (IsDictionary)
                        {
                            if (KeyName == ValueName)
                            {
                                DataContract.ThrowInvalidDataContractException(
                                    SR.Format(SR.DupKeyValueName, DataContract.GetClrTypeFullName(UnderlyingType), KeyName),
                                    UnderlyingType);
                            }
                            Debug.Assert(KeyName != null);
                            Debug.Assert(ValueName != null);
                            _itemContract = ClassDataContract.CreateClassDataContractForKeyValue(ItemType, Namespace, new string[] { KeyName, ValueName });
                            // Ensure that DataContract gets added to the static DataContract cache for dictionary items
                            DataContract.GetDataContract(ItemType);
                        }
                        else
                        {
                            _itemContract = DataContract.GetDataContract(ItemType);
                        }
                    }
                    return _itemContract;
                }
                set
                {
                    _itemContract = value;
                }
            }
 
            internal DataContract? SharedTypeContract
            {
                get => _sharedTypeContract;
                set => _sharedTypeContract = value;
            }
 
            internal string ItemName
            {
                get => _itemName;
                set => _itemName = value;
            }
 
            internal bool IsConstructorCheckRequired
            {
                get => _isConstructorCheckRequired;
                set => _isConstructorCheckRequired = value;
            }
 
            internal XmlDictionaryString CollectionItemName => _collectionItemName;
 
            internal string? KeyName
            {
                get => _keyName;
                set => _keyName = value;
            }
 
            internal string? ValueName
            {
                get => _valueName;
                set => _valueName = value;
            }
 
            internal bool IsDictionary => KeyName != null;
 
            internal string? SerializationExceptionMessage => _serializationExceptionMessage;
 
            internal string? DeserializationExceptionMessage => _deserializationExceptionMessage;
 
            internal XmlDictionaryString? ChildElementNamespace
            {
                get => _childElementNamespace;
                set => _childElementNamespace = value;
            }
 
            internal bool IsItemTypeNullable
            {
                get => _isItemTypeNullable;
                set => _isItemTypeNullable = value;
            }
 
            internal MethodInfo? GetEnumeratorMethod => _getEnumeratorMethod;
 
            internal MethodInfo? AddMethod => _addMethod;
 
            internal ConstructorInfo? Constructor => _constructor;
 
            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? InvalidCollectionInSharedContractMessage => _invalidCollectionInSharedContractMessage;
 
            internal bool ItemNameSetExplicit => _itemNameSetExplicit;
 
            internal XmlFormatCollectionWriterDelegate? XmlFormatWriterDelegate
            {
                get => _xmlFormatWriterDelegate;
                set => _xmlFormatWriterDelegate = value;
            }
 
            internal XmlFormatCollectionReaderDelegate? XmlFormatReaderDelegate
            {
                get => _xmlFormatReaderDelegate;
                set => _xmlFormatReaderDelegate = value;
            }
 
            internal XmlFormatGetOnlyCollectionReaderDelegate? XmlFormatGetOnlyCollectionReaderDelegate
            {
                get => _xmlFormatGetOnlyCollectionReaderDelegate;
                set => _xmlFormatGetOnlyCollectionReaderDelegate = value;
            }
 
            private delegate void IncrementCollectionCountDelegate(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context);
 
            private IncrementCollectionCountDelegate? _incrementCollectionCountDelegate;
 
            private static void DummyIncrementCollectionCount(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context) { }
 
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            internal void IncrementCollectionCount(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context)
            {
                if (_incrementCollectionCountDelegate == null)
                {
                    switch (Kind)
                    {
                        case CollectionKind.Collection:
                        case CollectionKind.List:
                        case CollectionKind.Dictionary:
                            {
                                _incrementCollectionCountDelegate = (x, o, c) =>
                                {
                                    c.IncrementCollectionCount(x, (ICollection)o);
                                };
                            }
                            break;
                        case CollectionKind.GenericCollection:
                        case CollectionKind.GenericList:
                            {
                                MethodInfo? buildIncrementCollectionCountDelegate = GetBuildIncrementCollectionCountGenericDelegate(ItemType);
                                _incrementCollectionCountDelegate = (IncrementCollectionCountDelegate)buildIncrementCollectionCountDelegate.Invoke(null, Array.Empty<object>())!;
                            }
                            break;
                        case CollectionKind.GenericDictionary:
                            {
                                MethodInfo? buildIncrementCollectionCountDelegate = GetBuildIncrementCollectionCountGenericDelegate(typeof(KeyValuePair<,>).MakeGenericType(ItemType.GetGenericArguments()));
                                _incrementCollectionCountDelegate = (IncrementCollectionCountDelegate)buildIncrementCollectionCountDelegate.Invoke(null, Array.Empty<object>())!;
                            }
                            break;
                        default:
                            // Do nothing.
                            _incrementCollectionCountDelegate = DummyIncrementCollectionCount;
                            break;
                    }
                }
 
                _incrementCollectionCountDelegate(xmlWriter, obj, context);
 
                [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod",
                    Justification = "The call to MakeGenericMethod is safe due to the fact that CollectionDataContractCriticalHelper.BuildIncrementCollectionCountDelegate<T> is not annotated.")]
                static MethodInfo GetBuildIncrementCollectionCountGenericDelegate(Type type) => BuildIncrementCollectionCountDelegateMethod.MakeGenericMethod(type);
            }
 
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod",
                Justification = "The call to MakeGenericMethod is safe due to the fact that CollectionDataContractCriticalHelper.BuildIncrementCollectionCountDelegate<T> is not annotated.")]
            private static MethodInfo GetBuildIncrementCollectionCountGenericDelegate(Type type) => BuildIncrementCollectionCountDelegateMethod.MakeGenericMethod(type);
 
            private static MethodInfo? s_buildIncrementCollectionCountDelegateMethod;
 
            private static MethodInfo BuildIncrementCollectionCountDelegateMethod =>
                s_buildIncrementCollectionCountDelegateMethod ??= typeof(CollectionDataContractCriticalHelper).GetMethod(nameof(BuildIncrementCollectionCountDelegate), Globals.ScanAllMembers)!;
 
            private static IncrementCollectionCountDelegate BuildIncrementCollectionCountDelegate<T>()
            {
                return (xmlwriter, obj, context) =>
                {
                    context.IncrementCollectionCountGeneric<T>(xmlwriter, (ICollection<T>)obj);
                };
            }
 
            private delegate IEnumerator CreateGenericDictionaryEnumeratorDelegate(IEnumerator enumerator);
 
            private CreateGenericDictionaryEnumeratorDelegate? _createGenericDictionaryEnumeratorDelegate;
 
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            internal IEnumerator GetEnumeratorForCollection(object obj)
            {
                IEnumerator enumerator = ((IEnumerable)obj).GetEnumerator();
                if (Kind == CollectionKind.GenericDictionary)
                {
                    if (_createGenericDictionaryEnumeratorDelegate == null)
                    {
                        Type[]? keyValueTypes = ItemType.GetGenericArguments();
                        MethodInfo buildCreateGenericDictionaryEnumerator = GetBuildCreateGenericDictionaryEnumeratorGenericMethod(keyValueTypes);
                        _createGenericDictionaryEnumeratorDelegate = (CreateGenericDictionaryEnumeratorDelegate)buildCreateGenericDictionaryEnumerator.Invoke(null, Array.Empty<object>())!;
                    }
 
                    enumerator = _createGenericDictionaryEnumeratorDelegate(enumerator);
                }
                else if (Kind == CollectionKind.Dictionary)
                {
                    enumerator = new DictionaryEnumerator(((IDictionary)obj).GetEnumerator());
                }
 
                return enumerator;
 
                [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod",
                Justification = "The call to MakeGenericMethod is safe due to the fact that CollectionDataContractCriticalHelper.BuildCreateGenericDictionaryEnumerator<K,V> is not annotated.")]
                static MethodInfo GetBuildCreateGenericDictionaryEnumeratorGenericMethod(Type[] keyValueTypes) => GetBuildCreateGenericDictionaryEnumeratorMethodInfo.MakeGenericMethod(keyValueTypes[0], keyValueTypes[1]);
            }
 
            [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
            [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
            internal Type GetCollectionElementType()
            {
                Debug.Assert(Kind != CollectionKind.Array, "GetCollectionElementType should not be called on Arrays");
                Debug.Assert(GetEnumeratorMethod != null, "GetEnumeratorMethod should be non-null for non-Arrays");
 
                Type? enumeratorType;
                if (Kind == CollectionKind.GenericDictionary)
                {
                    Type[] keyValueTypes = ItemType.GetGenericArguments();
                    enumeratorType = Globals.TypeOfGenericDictionaryEnumerator.MakeGenericType(keyValueTypes);
                }
                else if (Kind == CollectionKind.Dictionary)
                {
                    enumeratorType = Globals.TypeOfDictionaryEnumerator;
                }
                else
                {
                    enumeratorType = GetEnumeratorMethod.ReturnType;
                }
 
                MethodInfo? getCurrentMethod = enumeratorType.GetMethod(Globals.GetCurrentMethodName, BindingFlags.Instance | BindingFlags.Public, Type.EmptyTypes);
                if (getCurrentMethod == null)
                {
                    if (enumeratorType.IsInterface)
                    {
                        getCurrentMethod = XmlFormatGeneratorStatics.GetCurrentMethod;
                    }
                    else
                    {
                        Type ienumeratorInterface = Globals.TypeOfIEnumerator;
                        if (Kind == CollectionKind.GenericDictionary || Kind == CollectionKind.GenericCollection || Kind == CollectionKind.GenericEnumerable)
                        {
                            Type[] interfaceTypes = enumeratorType.GetInterfaces();
                            foreach (Type interfaceType in interfaceTypes)
                            {
                                if (interfaceType.IsGenericType
                                    && interfaceType.GetGenericTypeDefinition() == Globals.TypeOfIEnumeratorGeneric
                                    && interfaceType.GetGenericArguments()[0] == ItemType)
                                {
                                    ienumeratorInterface = interfaceType;
                                    break;
                                }
                            }
                        }
 
                        getCurrentMethod = GetTargetMethodWithName(Globals.GetCurrentMethodName, enumeratorType, ienumeratorInterface)!;
                    }
                }
 
                Type elementType = getCurrentMethod.ReturnType;
                return elementType;
            }
 
            private static MethodInfo? s_buildCreateGenericDictionaryEnumerator;
 
            private static MethodInfo GetBuildCreateGenericDictionaryEnumeratorMethodInfo =>
                s_buildCreateGenericDictionaryEnumerator ??= typeof(CollectionDataContractCriticalHelper).GetMethod(nameof(BuildCreateGenericDictionaryEnumerator), Globals.ScanAllMembers)!;
 
            private static CreateGenericDictionaryEnumeratorDelegate BuildCreateGenericDictionaryEnumerator<K, V>()
            {
                return (enumerator) =>
                {
                    return new GenericDictionaryEnumerator<K, V>((IEnumerator<KeyValuePair<K, V>>)enumerator);
                };
            }
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private DataContract? GetSharedTypeContract(Type type)
        {
            if (type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false))
            {
                return this;
            }
#pragma warning disable SYSLIB0050 // Type.IsSerializable is obsolete
            if (type.IsSerializable || type.IsDefined(Globals.TypeOfDataContractAttribute, false))
            {
                return new ClassDataContract(type);
            }
#pragma warning restore SYSLIB0050
            return null;
        }
 
        internal static bool IsCollectionInterface(Type type)
        {
            if (type.IsGenericType)
                type = type.GetGenericTypeDefinition();
            return ((IList<Type>)KnownInterfaces).Contains(type);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal static bool IsCollection(Type type)
        {
            return IsCollection(type, out _);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal static bool IsCollection(Type type, [NotNullWhen(true)] out Type? itemType)
        {
            return IsCollectionHelper(type, out itemType, true /*constructorRequired*/);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal static bool IsCollection(Type type, bool constructorRequired, bool skipIfReadOnlyContract)
        {
            return IsCollectionHelper(type, out _, constructorRequired, skipIfReadOnlyContract);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private static bool IsCollectionHelper(Type type, [NotNullWhen(true)] out Type? itemType, bool constructorRequired, bool skipIfReadOnlyContract = false)
        {
            if (type.IsArray && DataContract.GetBuiltInDataContract(type) == null)
            {
                itemType = type.GetElementType()!;
                return true;
            }
            return IsCollectionOrTryCreate(type, tryCreate: false, out _, out itemType, constructorRequired, skipIfReadOnlyContract);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal static bool TryCreate(Type type, [NotNullWhen(true)] out DataContract? dataContract)
        {
            return IsCollectionOrTryCreate(type, tryCreate: true, out dataContract!, out _, constructorRequired: true);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal static bool TryCreateGetOnlyCollectionDataContract(Type type, [NotNullWhen(true)] out DataContract? dataContract)
        {
            if (type.IsArray)
            {
                dataContract = new CollectionDataContract(type);
                return true;
            }
            else
            {
                return IsCollectionOrTryCreate(type, tryCreate: true, out dataContract!, out _, constructorRequired: false);
            }
        }
 
        // Once https://github.com/mono/linker/issues/1731 is fixed we can remove the suppression from here as it won't be needed any longer.
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:GetMethod",
            Justification = "The DynamicallyAccessedMembers declarations will ensure the interface methods will be preserved.")]
        internal static MethodInfo? GetTargetMethodWithName(string name,
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
            Type type,
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
            Type interfaceType)
        {
            Type? t = type.GetInterfaces().Where(it => it.Equals(interfaceType)).FirstOrDefault();
            return t?.GetMethod(name);
        }
 
        private static bool IsArraySegment(Type t)
        {
            return t.IsGenericType && (t.GetGenericTypeDefinition() == typeof(ArraySegment<>));
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataContract? dataContract, out Type itemType, bool constructorRequired, bool skipIfReadOnlyContract = false)
        {
            dataContract = null;
            itemType = Globals.TypeOfObject;
 
            if (DataContract.GetBuiltInDataContract(type) != null)
            {
                return HandleIfInvalidCollection(type, tryCreate, false/*hasCollectionDataContract*/, false/*isBaseTypeCollection*/,
                    SR.CollectionTypeCannotBeBuiltIn, null, ref dataContract);
            }
 
            MethodInfo? addMethod, getEnumeratorMethod;
            bool hasCollectionDataContract = IsCollectionDataContract(type);
            bool isReadOnlyContract = false;
            string? serializationExceptionMessage = null;
            string? deserializationExceptionMessage = null;
            Type? baseType = type.BaseType;
            bool isBaseTypeCollection = (baseType != null && baseType != Globals.TypeOfObject
                && baseType != Globals.TypeOfValueType && baseType != Globals.TypeOfUri) ? IsCollection(baseType) : false;
 
            // Avoid creating an invalid collection contract for Serializable types since we can create a ClassDataContract instead
#pragma warning disable SYSLIB0050 // Type.IsSerializable is obsolete
            bool createContractWithException = isBaseTypeCollection && !type.IsSerializable;
#pragma warning restore SYSLIB0050
 
            if (type.IsDefined(Globals.TypeOfDataContractAttribute, false))
            {
                return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException,
                    SR.CollectionTypeCannotHaveDataContract, null, ref dataContract);
            }
 
            if (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type) || IsArraySegment(type))
            {
                return false;
            }
 
            if (!Globals.TypeOfIEnumerable.IsAssignableFrom(type))
            {
                return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException,
                    SR.CollectionTypeIsNotIEnumerable, null, ref dataContract);
            }
 
            if (type.IsInterface)
            {
                Type interfaceTypeToCheck = type.IsGenericType ? type.GetGenericTypeDefinition() : type;
                Type[] knownInterfaces = KnownInterfaces;
                for (int i = 0; i < knownInterfaces.Length; i++)
                {
                    if (knownInterfaces[i] == interfaceTypeToCheck)
                    {
                        addMethod = null;
                        if (type.IsGenericType)
                        {
                            Type[] genericArgs = type.GetGenericArguments();
                            if (interfaceTypeToCheck == Globals.TypeOfIDictionaryGeneric)
                            {
                                itemType = Globals.TypeOfKeyValue.MakeGenericType(genericArgs);
                                addMethod = type.GetMethod(Globals.AddMethodName);
                                getEnumeratorMethod = Globals.TypeOfIEnumerableGeneric.MakeGenericType(Globals.TypeOfKeyValuePair.MakeGenericType(genericArgs)).GetMethod(Globals.GetEnumeratorMethodName)!;
                            }
                            else
                            {
                                itemType = genericArgs[0];
                                if (interfaceTypeToCheck == Globals.TypeOfICollectionGeneric || interfaceTypeToCheck == Globals.TypeOfIListGeneric)
                                {
                                    addMethod = Globals.TypeOfICollectionGeneric.MakeGenericType(itemType).GetMethod(Globals.AddMethodName);
                                }
                                getEnumeratorMethod = Globals.TypeOfIEnumerableGeneric.MakeGenericType(itemType).GetMethod(Globals.GetEnumeratorMethodName)!;
                            }
                        }
                        else
                        {
                            if (interfaceTypeToCheck == Globals.TypeOfIDictionary)
                            {
                                itemType = typeof(KeyValue<object, object>);
                                addMethod = type.GetMethod(Globals.AddMethodName);
                            }
                            else
                            {
                                itemType = Globals.TypeOfObject;
 
                                // IList has AddMethod
                                if (interfaceTypeToCheck == Globals.TypeOfIList)
                                {
                                    addMethod = type.GetMethod(Globals.AddMethodName);
                                }
                            }
 
                            getEnumeratorMethod = typeof(IEnumerable).GetMethod(Globals.GetEnumeratorMethodName)!;
                        }
                        if (tryCreate)
                            dataContract = new CollectionDataContract(type, (CollectionKind)(i + 1), itemType, getEnumeratorMethod, addMethod, null/*defaultCtor*/);
                        return true;
                    }
                }
            }
            ConstructorInfo? defaultCtor = null;
            if (!type.IsValueType)
            {
                defaultCtor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, Type.EmptyTypes);
                if (defaultCtor == null && constructorRequired)
                {
                    // All collection types could be considered read-only collections except collection types that are marked [Serializable].
                    // Collection types marked [Serializable] cannot be read-only collections for backward compatibility reasons.
                    // DataContract types and POCO types cannot be collection types, so they don't need to be factored in
#pragma warning disable SYSLIB0050 // Type.IsSerializable is obsolete
                    if (type.IsSerializable)
                    {
                        return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException,
                            SR.CollectionTypeDoesNotHaveDefaultCtor, null, ref dataContract);
                    }
#pragma warning restore SYSLIB0050
                    else
                    {
                        isReadOnlyContract = true;
                        GetReadOnlyCollectionExceptionMessages(type, hasCollectionDataContract, SR.CollectionTypeDoesNotHaveDefaultCtor, null, out serializationExceptionMessage, out deserializationExceptionMessage);
                    }
                }
            }
 
            Type? knownInterfaceType = null;
            CollectionKind kind = CollectionKind.None;
            bool multipleDefinitions = false;
            Type[] interfaceTypes = type.GetInterfaces();
            foreach (Type interfaceType in interfaceTypes)
            {
                Type interfaceTypeToCheck = interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType;
                Type[] knownInterfaces = KnownInterfaces;
                for (int i = 0; i < knownInterfaces.Length; i++)
                {
                    if (knownInterfaces[i] == interfaceTypeToCheck)
                    {
                        CollectionKind currentKind = (CollectionKind)(i + 1);
                        if (kind == CollectionKind.None || currentKind < kind)
                        {
                            kind = currentKind;
                            knownInterfaceType = interfaceType;
                            multipleDefinitions = false;
                        }
                        else if ((kind & currentKind) == currentKind)
                            multipleDefinitions = true;
                        break;
                    }
                }
            }
 
            if (kind == CollectionKind.None)
            {
                return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException,
                    SR.CollectionTypeIsNotIEnumerable, null, ref dataContract);
            }
 
            Debug.Assert(knownInterfaceType != null);
            if (kind == CollectionKind.Enumerable || kind == CollectionKind.Collection || kind == CollectionKind.GenericEnumerable)
            {
                if (multipleDefinitions)
                    knownInterfaceType = Globals.TypeOfIEnumerable;
                itemType = knownInterfaceType.IsGenericType ? knownInterfaceType.GetGenericArguments()[0] : Globals.TypeOfObject;
                GetCollectionMethods(type, knownInterfaceType, new Type[] { itemType },
                                     false /*addMethodOnInterface*/,
                                     out getEnumeratorMethod, out addMethod);
 
                Debug.Assert(getEnumeratorMethod != null);
 
                if (addMethod == null)
                {
                    // All collection types could be considered read-only collections except collection types that are marked [Serializable].
                    // Collection types marked [Serializable] cannot be read-only collections for backward compatibility reasons.
                    // DataContract types and POCO types cannot be collection types, so they don't need to be factored in.
#pragma warning disable SYSLIB0050 // Type.IsSerializable is obsolete
                    if (type.IsSerializable || skipIfReadOnlyContract)
                    {
                        return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException && !skipIfReadOnlyContract,
                            SR.CollectionTypeDoesNotHaveAddMethod, DataContract.GetClrTypeFullName(itemType), ref dataContract);
                    }
#pragma warning restore SYSLIB0050
                    else
                    {
                        isReadOnlyContract = true;
                        GetReadOnlyCollectionExceptionMessages(type, hasCollectionDataContract, SR.CollectionTypeDoesNotHaveAddMethod, DataContract.GetClrTypeFullName(itemType), out serializationExceptionMessage, out deserializationExceptionMessage);
                    }
                }
 
                if (tryCreate)
                {
                    dataContract = isReadOnlyContract ?
                        new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, serializationExceptionMessage, deserializationExceptionMessage) :
                        new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, addMethod, defaultCtor, !constructorRequired);
                }
            }
            else
            {
                if (multipleDefinitions)
                {
                    return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException,
                        SR.CollectionTypeHasMultipleDefinitionsOfInterface, KnownInterfaces[(int)kind - 1].Name, ref dataContract);
                }
                Type[]? addMethodTypeArray = null;
                switch (kind)
                {
                    case CollectionKind.GenericDictionary:
                        addMethodTypeArray = knownInterfaceType.GetGenericArguments();
                        bool isOpenGeneric = knownInterfaceType.IsGenericTypeDefinition
                            || (addMethodTypeArray[0].IsGenericParameter && addMethodTypeArray[1].IsGenericParameter);
                        itemType = isOpenGeneric ? Globals.TypeOfKeyValue : Globals.TypeOfKeyValue.MakeGenericType(addMethodTypeArray);
                        break;
                    case CollectionKind.Dictionary:
                        addMethodTypeArray = new Type[] { Globals.TypeOfObject, Globals.TypeOfObject };
                        itemType = Globals.TypeOfKeyValue.MakeGenericType(addMethodTypeArray);
                        break;
                    case CollectionKind.GenericList:
                    case CollectionKind.GenericCollection:
                        addMethodTypeArray = knownInterfaceType.GetGenericArguments();
                        itemType = addMethodTypeArray[0];
                        break;
                    case CollectionKind.List:
                        itemType = Globals.TypeOfObject;
                        addMethodTypeArray = new Type[] { itemType };
                        break;
                }
 
                if (tryCreate)
                {
                    Debug.Assert(addMethodTypeArray != null);
                    GetCollectionMethods(type, knownInterfaceType, addMethodTypeArray,
                                     true /*addMethodOnInterface*/,
                                     out getEnumeratorMethod, out addMethod);
 
                    Debug.Assert(getEnumeratorMethod != null);
 
                    dataContract = isReadOnlyContract ?
                        new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, serializationExceptionMessage, deserializationExceptionMessage) :
                        new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, addMethod, defaultCtor, !constructorRequired);
                }
            }
 
            return !(isReadOnlyContract && skipIfReadOnlyContract);
        }
 
        internal static bool IsCollectionDataContract(Type type)
        {
            return type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private static bool HandleIfInvalidCollection(Type type, bool tryCreate, bool hasCollectionDataContract, bool createContractWithException, string message, string? param, ref DataContract? dataContract)
        {
            if (hasCollectionDataContract)
            {
                if (tryCreate)
                    throw new InvalidDataContractException(GetInvalidCollectionMessage(message, SR.Format(SR.InvalidCollectionDataContract, DataContract.GetClrTypeFullName(type)), param));
                return true;
            }
 
            if (createContractWithException)
            {
                if (tryCreate)
                    dataContract = new CollectionDataContract(type, GetInvalidCollectionMessage(message, SR.Format(SR.InvalidCollectionType, DataContract.GetClrTypeFullName(type)), param));
                return true;
            }
 
            return false;
        }
 
        private static void GetReadOnlyCollectionExceptionMessages(Type type, bool hasCollectionDataContract, string message, string? param, out string serializationExceptionMessage, out string deserializationExceptionMessage)
        {
            serializationExceptionMessage = GetInvalidCollectionMessage(message, SR.Format(hasCollectionDataContract ? SR.InvalidCollectionDataContract : SR.InvalidCollectionType, GetClrTypeFullName(type)), param);
            deserializationExceptionMessage = GetInvalidCollectionMessage(message, SR.Format(SR.ReadOnlyCollectionDeserialization, GetClrTypeFullName(type)), param);
        }
 
        private static string GetInvalidCollectionMessage(string message, string nestedMessage, string? param)
        {
            return (param == null) ? SR.Format(message, nestedMessage) : SR.Format(message, nestedMessage, param);
        }
 
        // Once https://github.com/mono/linker/issues/1731 is fixed we can remove the suppression from here as it won't be needed any longer.
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:GetMethod",
            Justification = "The DynamicallyAccessedMembers declarations will ensure the interface methods will be preserved.")]
        private static void FindCollectionMethodsOnInterface(
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
            Type type,
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
            Type interfaceType,
            ref MethodInfo? addMethod, ref MethodInfo? getEnumeratorMethod)
        {
            Type? t = type.GetInterfaces().Where(it => it.Equals(interfaceType)).FirstOrDefault();
            if (t != null)
            {
                addMethod = t.GetMethod(Globals.AddMethodName) ?? addMethod;
                getEnumeratorMethod = t.GetMethod(Globals.GetEnumeratorMethodName) ?? getEnumeratorMethod;
            }
        }
 
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private static void GetCollectionMethods(
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
            Type type,
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
            Type interfaceType,
            Type[] addMethodTypeArray, bool addMethodOnInterface, out MethodInfo? getEnumeratorMethod, out MethodInfo? addMethod)
        {
            addMethod = getEnumeratorMethod = null;
 
            if (addMethodOnInterface)
            {
                addMethod = type.GetMethod(Globals.AddMethodName, BindingFlags.Instance | BindingFlags.Public, addMethodTypeArray);
                if (addMethod == null || addMethod.GetParameters()[0].ParameterType != addMethodTypeArray[0])
                {
                    FindCollectionMethodsOnInterface(type, interfaceType, ref addMethod, ref getEnumeratorMethod);
                    if (addMethod == null)
                    {
                        Type[] parentInterfaceTypes = interfaceType.GetInterfaces();
                        // The for loop below depeneds on the order for the items in parentInterfaceTypes, which
                        // doesnt' seem right. But it's the behavior of DCS on the full framework.
                        // Sorting the array to make sure the behavior is consistent with Desktop's.
                        Array.Sort(parentInterfaceTypes, (x, y) => string.Compare(x.FullName, y.FullName));
                        foreach (Type parentInterfaceType in parentInterfaceTypes)
                        {
                            if (IsKnownInterface(parentInterfaceType))
                            {
                                FindCollectionMethodsOnInterface(type, parentInterfaceType, ref addMethod, ref getEnumeratorMethod);
                                if (addMethod == null)
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                // GetMethod returns Add() method with parameter closest matching T in assignability/inheritance chain
                addMethod = type.GetMethod(Globals.AddMethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, addMethodTypeArray);
            }
 
            if (getEnumeratorMethod == null)
            {
                getEnumeratorMethod = type.GetMethod(Globals.GetEnumeratorMethodName, BindingFlags.Instance | BindingFlags.Public, Type.EmptyTypes);
                if (getEnumeratorMethod == null || !Globals.TypeOfIEnumerator.IsAssignableFrom(getEnumeratorMethod.ReturnType))
                {
                    Type? ienumerableInterface =
                        interfaceType.GetInterfaces().Where(t => t.FullName!.StartsWith("System.Collections.Generic.IEnumerable")).FirstOrDefault() ??
                        Globals.TypeOfIEnumerable;
                    getEnumeratorMethod = GetIEnumerableGetEnumeratorMethod(type, ienumerableInterface);
                }
            }
        }
 
        private static MethodInfo? GetIEnumerableGetEnumeratorMethod(
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
            Type type,
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
            Type ienumerableInterface)
                => GetTargetMethodWithName(Globals.GetEnumeratorMethodName, type, ienumerableInterface);
 
        private static bool IsKnownInterface(Type type)
        {
            Type typeToCheck = type.IsGenericType ? type.GetGenericTypeDefinition() : type;
            foreach (Type knownInterfaceType in KnownInterfaces)
            {
                if (typeToCheck == knownInterfaceType)
                {
                    return true;
                }
            }
            return false;
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal override DataContract BindGenericParameters(DataContract[] paramContracts, Dictionary<DataContract, DataContract>? boundContracts = null)
        {
            DataContract boundContract;
            if (boundContracts != null && boundContracts.TryGetValue(this, out boundContract!))
                return boundContract;
 
            // 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 type = UnderlyingType;
            Type[] paramTypes = type.GetGenericArguments();
            for (int i = 0; i < paramTypes.Length; i++)
            {
                if (paramTypes[i].IsGenericParameter)
                    paramTypes[i] = paramContracts[paramTypes[i].GenericParameterPosition].UnderlyingType;
            }
            Type boundType = type.MakeGenericType(paramTypes);
 
            CollectionDataContract boundCollectionContract = new CollectionDataContract(boundType);
            boundContracts ??= new Dictionary<DataContract, DataContract>();
            boundContracts.Add(this, boundCollectionContract);
            boundCollectionContract.ItemContract = ItemContract.BindGenericParameters(paramContracts, boundContracts);
            boundCollectionContract.IsItemTypeNullable = !boundCollectionContract.ItemContract.IsValueType;
            boundCollectionContract.ItemName = ItemNameSetExplicit ? ItemName : boundCollectionContract.ItemContract.XmlName.Name;
            boundCollectionContract.KeyName = KeyName;
            boundCollectionContract.ValueName = ValueName;
            boundCollectionContract.XmlName = CreateQualifiedName(DataContract.ExpandGenericParameters(XmlConvert.DecodeName(XmlName.Name), new GenericNameProvider(DataContract.GetClrTypeFullName(UnderlyingType), paramContracts)),
                IsCollectionDataContract(UnderlyingType) ? XmlName.Namespace : DataContract.GetCollectionNamespace(boundCollectionContract.ItemContract.XmlName.Namespace));
            return boundCollectionContract;
        }
 
        internal override DataContract GetValidContract(bool verifyConstructor = false)
        {
            if (verifyConstructor && IsConstructorCheckRequired)
            {
                CheckConstructor();
                return this;
            }
 
            if (InvalidCollectionInSharedContractMessage != null)
                throw new InvalidDataContractException(InvalidCollectionInSharedContractMessage);
            return this;
        }
 
        private void CheckConstructor()
        {
            if (Constructor == null)
            {
                throw new InvalidDataContractException(SR.Format(SR.CollectionTypeDoesNotHaveDefaultCtor, DataContract.GetClrTypeFullName(UnderlyingType)));
            }
            else
            {
                IsConstructorCheckRequired = false;
            }
        }
 
        internal override bool IsValidContract()
        {
            return (InvalidCollectionInSharedContractMessage == null);
        }
 
        /// <SecurityNote>
        /// Review - calculates whether this collection 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)
        {
            if (!IsTypeVisible(UnderlyingType))
            {
                if (securityException != null)
                {
                    throw new SecurityException(SR.Format(
                                SR.PartialTrustCollectionContractTypeNotPublic,
                                DataContract.GetClrTypeFullName(UnderlyingType)),
                            securityException);
                }
                return true;
            }
            if (ItemType != null && !IsTypeVisible(ItemType))
            {
                if (securityException != null)
                {
                    throw new SecurityException(SR.Format(
                                SR.PartialTrustCollectionContractTypeNotPublic,
                                DataContract.GetClrTypeFullName(ItemType)),
                            securityException);
                }
                return true;
            }
            if (ConstructorRequiresMemberAccess(Constructor))
            {
                if (securityException != null)
                {
                    throw new SecurityException(SR.Format(
                                SR.PartialTrustCollectionContractNoPublicConstructor,
                                DataContract.GetClrTypeFullName(UnderlyingType)),
                            securityException);
                }
                return true;
            }
            if (MethodRequiresMemberAccess(AddMethod))
            {
                if (securityException != null)
                {
                    throw new SecurityException(SR.Format(
                                   SR.PartialTrustCollectionContractAddMethodNotPublic,
                                   DataContract.GetClrTypeFullName(UnderlyingType),
                                   AddMethod!.Name),
                               securityException);
                }
                return true;
            }
 
            return false;
        }
 
        /// <SecurityNote>
        /// Review - calculates whether this collection 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)
        {
            if (!IsTypeVisible(UnderlyingType))
            {
                if (securityException != null)
                {
                    throw new SecurityException(SR.Format(
                                SR.PartialTrustCollectionContractTypeNotPublic,
                                DataContract.GetClrTypeFullName(UnderlyingType)),
                            securityException);
                }
                return true;
            }
            if (ItemType != null && !IsTypeVisible(ItemType))
            {
                if (securityException != null)
                {
                    throw new SecurityException(SR.Format(
                                SR.PartialTrustCollectionContractTypeNotPublic,
                                DataContract.GetClrTypeFullName(ItemType)),
                            securityException);
                }
                return true;
            }
 
            return false;
        }
 
        [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 CollectionDataContract dataContract)
                {
                    bool thisItemTypeIsNullable = (ItemContract == null) ? false : !ItemContract.IsValueType;
                    bool otherItemTypeIsNullable = (dataContract.ItemContract == null) ? false : !dataContract.ItemContract.IsValueType;
                    return ItemName == dataContract.ItemName &&
                        (IsItemTypeNullable || thisItemTypeIsNullable) == (dataContract.IsItemTypeNullable || otherItemTypeIsNullable) &&
                        ItemContract != null && ItemContract.Equals(dataContract.ItemContract, checkedContracts);
                }
            }
            return false;
        }
 
        public override int GetHashCode() => base.GetHashCode();
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext? context)
        {
            Debug.Assert(context != null);
 
            // IsGetOnlyCollection value has already been used to create current collectiondatacontract, value can now be reset.
            context.IsGetOnlyCollection = false;
            XmlFormatWriterDelegate(xmlWriter, obj, context, this);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        internal override object? ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext? context)
        {
            Debug.Assert(context != null);
 
            xmlReader.Read();
            object? o = null;
            if (context.IsGetOnlyCollection)
            {
                // IsGetOnlyCollection value has already been used to create current collectiondatacontract, value can now be reset.
                context.IsGetOnlyCollection = false;
                XmlFormatGetOnlyCollectionReaderDelegate(xmlReader, context, CollectionItemName, Namespace, this);
            }
            else
            {
                o = XmlFormatReaderDelegate(xmlReader, context, CollectionItemName, Namespace, this);
            }
            xmlReader.ReadEndElement();
            return o;
        }
 
        internal sealed class DictionaryEnumerator : IEnumerator<KeyValue<object, object?>>
        {
            private readonly IDictionaryEnumerator _enumerator;
 
            public DictionaryEnumerator(IDictionaryEnumerator enumerator)
            {
                _enumerator = enumerator;
            }
 
            public void Dispose()
            {
                GC.SuppressFinalize(this);
            }
 
            public bool MoveNext()
            {
                return _enumerator.MoveNext();
            }
 
            public KeyValue<object, object?> Current => new KeyValue<object, object?>(_enumerator.Key, _enumerator.Value);
 
            object System.Collections.IEnumerator.Current => Current;
 
            public void Reset()
            {
                _enumerator.Reset();
            }
        }
 
        internal sealed class GenericDictionaryEnumerator<K, V> : IEnumerator<KeyValue<K, V>>
        {
            private readonly IEnumerator<KeyValuePair<K, V>> _enumerator;
 
            public GenericDictionaryEnumerator(IEnumerator<KeyValuePair<K, V>> enumerator)
            {
                _enumerator = enumerator;
            }
 
            public void Dispose()
            {
                GC.SuppressFinalize(this);
            }
 
            public bool MoveNext()
            {
                return _enumerator.MoveNext();
            }
 
            public KeyValue<K, V> Current
            {
                get
                {
                    KeyValuePair<K, V> current = _enumerator.Current;
                    return new KeyValue<K, V>(current.Key, current.Value);
                }
            }
 
            object System.Collections.IEnumerator.Current => Current;
 
            public void Reset()
            {
                _enumerator.Reset();
            }
        }
    }
}