/// <summary>
 /// Cache an extended type
 /// </summary>
 /// <param name="type">The extended type to cache</param>
 /// <param name="options">The options for the extended type</param>
 public static void CacheType(ExtendedType type, TypeSupportOptions options)
 {
     _cacheLock.Wait();
     try
     {
         var key = GenerateKey(type.Type, options);
         if (!Instance.CachedTypes.ContainsKey(key))
         {
             Instance.CachedTypes.Add(key, type);
             if (key.Options == TypeSupportOptions.All)
             {
                 // remove any types with options less than all
                 RemoveLowerOptions(type.Type);
             }
         }
     }
     finally
     {
         _cacheLock.Release();
     }
 }
Example #2
0
 /// <summary>
 /// Inspects a type
 /// </summary>
 /// <param name="extendedType">The extended type to inspect</param>
 /// <param name="options">The options to use for inspection</param>
 internal TypeInspector(ExtendedType extendedType, TypeSupportOptions options)
 {
     _extendedType = extendedType;
     _options      = options;
 }
        /// <summary>
        /// Create a new, empty object of a given type
        /// </summary>
        /// <param name="type">The type of object to construct</param>
        /// <param name="typeRegistry">A type registry for constructing unknown types</param>
        /// <param name="initializer">An optional initializer to use to create the object</param>
        /// <param name="dimensions">For array types, the dimensions of the array to create</param>
        /// <returns></returns>
        public object CreateEmptyObject(Type type, TypeRegistry typeRegistry, Func <object> initializer, params object[] dimensions)
        {
            var objectType = type;

            if (initializer != null)
            {
                return(initializer());
            }

            // check the type registry for a custom type mapping
            if (typeRegistry?.ContainsType(objectType) == true)
            {
                objectType = typeRegistry.GetMapping(objectType);
            }
            // check the type registry for a custom type factory
            if (typeRegistry?.ContainsFactoryType(objectType) == true)
            {
                return(typeRegistry.GetFactory(objectType).Invoke());
            }

            var typeSupport = new ExtendedType(objectType);

            // if we are asked to create an instance of an interface, try to initialize using a valid concrete type
            if (typeSupport.IsInterface && !typeSupport.IsEnumerable)
            {
                // try a known concrete type from typeSupport
                var concreteType = typeSupport.KnownConcreteTypes.FirstOrDefault();
                if (concreteType == null)
                {
                    throw new TypeSupportException(objectType, $"Unable to locate a concrete type for '{typeSupport.Type.FullName}'! Cannot create instance.");
                }

                typeSupport = new ExtendedType(concreteType);
            }

            if (typeSupport.IsArray)
            {
                object[] createParameters = dimensions;
                // we also want to allow passing of collections/lists/arrays so do some conversion first
                if (createParameters?.Length > 0)
                {
                    var parameterType = createParameters[0].GetType();
                    if (parameterType.IsArray)
                    {
                        var ar = (Array)createParameters[0];
                        createParameters = ar.Cast <object>().ToArray();
                    }
                    else if (typeof(ICollection).IsAssignableFrom(parameterType) ||
                             typeof(IList).IsAssignableFrom(parameterType))
                    {
                        var ar = (ICollection)createParameters[0];
                        createParameters = ar.Cast <object>().ToArray();
                    }
                }
                if (createParameters != null && createParameters.Length > 1)
                {
                    createParameters.Reverse();
                }
                if (createParameters == null || createParameters.Length == 0)
                {
                    createParameters = new object[] { 0 }
                }
                ;
                return(Activator.CreateInstance(typeSupport.Type, createParameters));
            }
            else if (typeSupport.IsDictionary)
            {
                var genericType = typeSupport.Type.GetGenericArguments().ToList();
                if (genericType.Count != 2)
                {
                    throw new TypeSupportException(objectType, "IDictionary should contain 2 element types.");
                }
                Type[] typeArgs = { genericType[0], genericType[1] };

                var listType      = typeof(Dictionary <,>).MakeGenericType(typeArgs);
                var newDictionary = Activator.CreateInstance(listType) as IDictionary;
                return(newDictionary);
            }
            else if (typeSupport.IsEnumerable && !typeSupport.IsImmutable)
            {
                var genericType = typeSupport.Type.GetGenericArguments().FirstOrDefault();
                // test specifically for ICollection and List
                var isGenericCollection = typeSupport.Type.IsGenericType &&
                                          typeSupport.Type.GetGenericTypeDefinition() == typeof(ICollection <>);
                var isGenericList = typeSupport.Type.IsGenericType &&
                                    typeSupport.Type.GetGenericTypeDefinition() == typeof(List <>);
                var isGenericIList = typeSupport.Type.IsGenericType &&
                                     typeSupport.Type.GetGenericTypeDefinition() == typeof(IList <>);
                if (!isGenericList && !isGenericIList && !isGenericCollection)
                {
                    var constructors = typeSupport.Type.GetConstructors(ConstructorOptions.All);
                    if (typeSupport.HasEmptyConstructor)
                    {
                        var newList = Activator.CreateInstance(typeSupport.Type);
                        return(newList);
                    }
                    else if (typeSupport.Constructors.Any())
                    {
                        // special handling is required here as custom collections can't be properly
                        // initialized using FormatterServices.GetUninitializedObject()
                        var constructor = typeSupport.Constructors.First();
                        var parameters  = constructor.GetParameters();
                        var param       = new List <object>();
                        // initialize using defaults for constructor parameters
                        foreach (var p in parameters)
                        {
                            param.Add(CreateEmptyObject(p.ParameterType));
                        }
                        var newList = Activator.CreateInstance(typeSupport.Type, param.ToArray());
                        return(newList);
                    }
                }
                else if (genericType != null)
                {
                    var listType = typeof(List <>).MakeGenericType(genericType);
                    var newList  = (IList)Activator.CreateInstance(listType);
                    return(newList);
                }
                return(Enumerable.Empty <object>());
            }
            else if (typeSupport.Type.ContainsGenericParameters)
            {
                // create a generic type and create an instance
                // to accomplish this, we need to create a new generic type using the type arguments from the interface
                // and the concrete class definition. voodoo!
                var    originalTypeSupport = new ExtendedType(objectType);
                var    genericArguments    = originalTypeSupport.Type.GetGenericArguments();
                var    newType             = typeSupport.Type.MakeGenericType(genericArguments);
                object newObject           = null;
                if (typeSupport.HasEmptyConstructor)
                {
                    newObject = Activator.CreateInstance(newType);
                }
                else
                {
                    newObject = FormatterServices.GetUninitializedObject(newType);
                }
                return(newObject);
            }
            else if (typeSupport.HasEmptyConstructor && !typeSupport.Type.ContainsGenericParameters)
            {
                return(Activator.CreateInstance(typeSupport.Type));
            }
            else if (typeSupport.IsImmutable)
            {
                return(null);
            }

            return(FormatterServices.GetUninitializedObject(typeSupport.Type));
        }
    }