/// <summary>
        /// If unable to resolve the type, this will throw an exception when ignoreAnyInvalidTypes is false; otherwise return null when it's true.
        /// </summary>
        public Type GetType(string typeName, bool ignoreAnyInvalidTypes)
        {
            if (string.IsNullOrWhiteSpace(typeName))
            {
                throw new ArgumentException($"Null/blank {nameof(typeName)} specified");
            }

            if (_typeLookupCache.TryGetValue(typeName, out var cachedResult))
            {
                return(cachedResult);
            }

            return(_typeLookupCache.GetOrAdd(typeName, _reader.GetType(typeName, ignoreAnyInvalidTypes)));
        }
예제 #2
0
        private object ReadNextArray(bool ignoreAnyInvalidTypes)
        {
            var elementTypeName = ReadNextTypeName(out var typeNameReferenceID);

            if (elementTypeName == null)
            {
                throw new InvalidSerialisationDataFormatException("Null array element type names should not exist in object data since there is a Null binary serialisation data type");
            }

            // 2021-06-17 Dion: We might not know the element type here if we're dealing with a newer type than what we have locally. In that case we can allow
            // _typeAnalyser.GetType to fail IF ignoreAnyInvalidTypes is true. We won't need to allocate any array if we're ignoring invalid types, but we will
            // need to read the rest of the data for the array anyway so we can skip over it.
            // These lookups will be cached by the_typeAnalyser, which can help (vs calling Type.GetType every time)
            var elementTypeIfKnown = _typeAnalyser.GetType(elementTypeName, ignoreAnyInvalidTypes);
            var lengthDataType     = ReadNextDataType();
            int length;

            if (lengthDataType == BinarySerialisationDataType.Int32_8)
            {
                length = ReadNext();
            }
            else if (lengthDataType == BinarySerialisationDataType.Int32_16)
            {
                length = ReadNextInt16();
            }
            else if (lengthDataType == BinarySerialisationDataType.Int32_24)
            {
                length = ReadNextInt24();
            }
            else if (lengthDataType == BinarySerialisationDataType.Int32)
            {
                length = ReadNextInt();
            }
            else
            {
                throw new InvalidSerialisationDataFormatException("Unexpected BinarySerialisationDataType for Array length: " + lengthDataType);
            }

            // When an array contains enum elements, the underlying value is written by the serialisation process and so we'll need to read that value back out and then cast it to
            // the enum type, otherwise the array SetValue call will fail. In order to do that, we need to check now whether the element type is an enum OR if the element type is
            // a Nullable enum because the same rules apply to that (a Nullable Nullable enum is not supported, so there is no recursive checks required).
            Type enumCastTargetTypeIfRequired;

            if (elementTypeIfKnown == null || length == 0)
            {
                enumCastTargetTypeIfRequired = null;
            }
            else
            {
                if (elementTypeIfKnown.IsEnum)
                {
                    enumCastTargetTypeIfRequired = elementTypeIfKnown;
                }
                else
                {
                    var nullableInnerTypeIfApplicable = Nullable.GetUnderlyingType(elementTypeIfKnown);
                    enumCastTargetTypeIfRequired = ((nullableInnerTypeIfApplicable != null) && nullableInnerTypeIfApplicable.IsEnum) ? nullableInnerTypeIfApplicable : null;
                }
            }

            // 2021-06-17 Dion: Just read in all the items without storing them in an array if we don't have the target type available (which will be the case
            // if the element type is not known and ignoreAnyInvalidTypes is true)
            Array itemsIfApplicable = elementTypeIfKnown != null
                                ? Array.CreateInstance(elementTypeIfKnown, length)
                                : null;

            for (var index = 0; index < length; index++)
            {
                var element = Read(ignoreAnyInvalidTypes, elementTypeIfKnown);
                if ((enumCastTargetTypeIfRequired != null) && (element != null))
                {
                    if (element is byte b)
                    {
                        element = Enum.ToObject(enumCastTargetTypeIfRequired, b);
                    }
                    else if (element is short s)
                    {
                        element = Enum.ToObject(enumCastTargetTypeIfRequired, s);
                    }
                    else if (element is int i)
                    {
                        element = Enum.ToObject(enumCastTargetTypeIfRequired, i);
                    }
                    else if (element is long l)
                    {
                        element = Enum.ToObject(enumCastTargetTypeIfRequired, l);
                    }
                    else if (element is sbyte sb)
                    {
                        element = Enum.ToObject(enumCastTargetTypeIfRequired, sb);
                    }
                    else if (element is ushort us)
                    {
                        element = Enum.ToObject(enumCastTargetTypeIfRequired, us);
                    }
                    else if (element is uint ui)
                    {
                        element = Enum.ToObject(enumCastTargetTypeIfRequired, ui);
                    }
                    else if (element is ulong ul)
                    {
                        element = Enum.ToObject(enumCastTargetTypeIfRequired, ul);
                    }
                    else
                    {
                        element = Enum.ToObject(enumCastTargetTypeIfRequired, element);
                    }
                }
                itemsIfApplicable?.SetValue(element, index);
            }

            // If the BinarySerialisationWriter is configured to optimise for wide circular references then there may be some content-for-delay-populated-objects here
            // after the array (but if it was configured for trees then there won't be any deferred object populations and we'll go straight to ArrayEnd)
            var nextEntryType = ReadNextDataType();

            while (nextEntryType != BinarySerialisationDataType.ArrayEnd)
            {
                if (nextEntryType != BinarySerialisationDataType.ObjectStart)
                {
                    throw new InvalidSerialisationDataFormatException($"After array elements, expected either {nameof(BinarySerialisationDataType.ArrayEnd)} or {nameof(BinarySerialisationDataType.ObjectStart)} (for deferred references)");
                }
                ReadNextObject(ignoreAnyInvalidTypes, toPopulateDeferredInstance: true);
                nextEntryType = ReadNextDataType();
            }
            if (nextEntryType != BinarySerialisationDataType.ArrayEnd)
            {
                throw new InvalidSerialisationDataFormatException($"Expected {nameof(BinarySerialisationDataType.ArrayEnd)} was not encountered");
            }
            return(itemsIfApplicable);
        }