Пример #1
0
 public ServiceKey(Type serviceType, ServiceDefinition definition)
 {
     ServiceType   = serviceType;
     ImplementType = definition.GetImplementType();
 }
Пример #2
0
        private object GetServiceInstance(Type serviceType, ServiceDefinition serviceDefinition)
        {
            if (serviceDefinition.ImplementationInstance != null)
            {
                return(serviceDefinition.ImplementationInstance);
            }

            if (serviceDefinition.ImplementationFactory != null)
            {
                return(serviceDefinition.ImplementationFactory.Invoke(this));
            }

            var implementType = (serviceDefinition.ImplementType ?? serviceType);

            if (implementType.IsInterface || implementType.IsAbstract)
            {
                throw new InvalidOperationException($"invalid service registered, serviceType: {serviceType.FullName}, implementType: {serviceDefinition.ImplementType}");
            }

            if (implementType.IsGenericType)
            {
                implementType = implementType.MakeGenericType(serviceType.GetGenericArguments());
            }

            var newFunc = CacheUtil.TypeNewFuncCache.GetOrAdd(implementType, (serviceContainer) =>
            {
                if (
                    CacheUtil.TypeEmptyConstructorFuncCache.TryGetValue(implementType, out var emptyFunc))
                {
                    return(emptyFunc.Invoke());
                }

                var ctor = CacheUtil.TypeConstructorCache.GetOrAdd(implementType, t =>
                {
                    var ctorInfos = t.GetConstructors(BindingFlags.Instance | BindingFlags.Public);
                    if (ctorInfos.Length == 0)
                    {
                        return(null);
                    }

                    ConstructorInfo ctorInfo;
                    if (ctorInfos.Length == 1)
                    {
                        ctorInfo = ctorInfos[0];
                    }
                    else
                    {
                        // TODO: try find best ctor
                        ctorInfo = ctorInfos
                                   .OrderBy(_ => _.GetParameters().Length)
                                   .First();
                    }

                    return(ctorInfo);
                });
                if (ctor == null)
                {
                    throw new InvalidOperationException(
                        $"service {serviceType.FullName} does not have any public constructors");
                }

                var parameters = ctor.GetParameters();
                if (parameters.Length == 0)
                {
                    var func00 = Expression.Lambda <Func <object> >(Expression.New(ctor)).Compile();
                    CacheUtil.TypeEmptyConstructorFuncCache.TryAdd(implementType, func00);
                    return(func00.Invoke());
                }

                var ctorParams = new object[parameters.Length];
                for (var index = 0; index < parameters.Length; index++)
                {
                    var param = serviceContainer.GetService(parameters[index].ParameterType);
                    if (param == null && parameters[index].HasDefaultValue)
                    {
                        param = parameters[index].DefaultValue;
                    }
                    ctorParams[index] = param;
                }

                var func = CacheUtil.TypeConstructorFuncCache.GetOrAdd(implementType, t =>
                {
                    if (!CacheUtil.TypeConstructorCache.TryGetValue(t, out var ctorInfo))
                    {
                        return(null);
                    }

                    var innerParameters     = ctorInfo.GetParameters();
                    var parameterExpression = Expression.Parameter(typeof(object[]), "arguments"); // create parameter Expression
                    var argExpressions      = new Expression[innerParameters.Length];              // array that will contains parameter expessions
                    for (var i = 0; i < innerParameters.Length; i++)
                    {
                        var indexedAccess = Expression.ArrayIndex(parameterExpression, Expression.Constant(i));

                        if (!innerParameters[i].ParameterType.IsClass)                                                  // check if parameter is a value type
                        {
                            var localVariable = Expression.Variable(innerParameters[i].ParameterType, "localVariable"); // if so - we should create local variable that will store paraameter value

                            var block = Expression.Block(new[] { localVariable },
                                                         Expression.IfThenElse(Expression.Equal(indexedAccess, Expression.Constant(null)),
                                                                               Expression.Assign(localVariable, Expression.Default(innerParameters[i].ParameterType)),
                                                                               Expression.Assign(localVariable, Expression.Convert(indexedAccess, innerParameters[i].ParameterType))
                                                                               ),
                                                         localVariable
                                                         );

                            argExpressions[i] = block;
                        }
                        else
                        {
                            argExpressions[i] = Expression.Convert(indexedAccess, innerParameters[i].ParameterType);
                        }
                    }
                    var newExpression = Expression.New(ctorInfo, argExpressions); // create expression that represents call to specified ctor with the specified arguments.

                    return(Expression.Lambda <Func <object[], object> >(newExpression, parameterExpression)
                           .Compile());
                });
                return(func.Invoke(ctorParams));
            });

            return(newFunc?.Invoke(this));
        }