You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Zero/ZeroLevel/Services/DependencyInjection/Internal/ConstructorMetadata.cs

113 lines
5.0 KiB

6 years ago
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace ZeroLevel.Patterns.DependencyInjection
{
/// <summary>
/// Метаданные конструктора
/// </summary>
internal class ConstructorMetadata
{
public ConstructorInfo Constructor { get; }
private IReadOnlyList<ConstructorParameter> Parameters { get; }
private readonly IContainer _parent;
public ConstructorMetadata(Container parent, ConstructorInfo info)
{
_parent = parent;
Constructor = info;
Parameters = info.
GetParameters().
Select(p =>
{
var parameterAttribute = p.GetCustomAttribute<ParameterAttribute>();
var resolveAttribute = p.GetCustomAttribute<ResolveAttribute>();
var kind = (parameterAttribute != null) ? ConstructorParameterKind.Parameter :
(resolveAttribute != null) ? ConstructorParameterKind.Resolve : ConstructorParameterKind.None;
return new ConstructorParameter
{
Type = p.ParameterType,
ParameterKind = kind,
ParameterResolveName = (kind == ConstructorParameterKind.Parameter) ? parameterAttribute?.Name ?? p.Name :
(kind == ConstructorParameterKind.Resolve) ? resolveAttribute?.ResolveName : null,
ParameterResolveType = (kind == ConstructorParameterKind.Parameter) ? parameterAttribute?.Type ?? p.ParameterType :
(kind == ConstructorParameterKind.Resolve) ? resolveAttribute?.ContractType ?? p.ParameterType : null,
IsNullable = IsNullable(p.ParameterType)
};
}).ToList();
}
private static bool IsNullable(Type type)
{
if (!type.IsValueType) return true; // ref-type
if (Nullable.GetUnderlyingType(type) != null) return true; // Nullable<T>
return false; // value-type
}
/// <summary>
/// Определение, подходит ли конструктор под указанные аргументы
/// </summary>
/// <param name="args">Аргументы</param>
/// <param name="parameters">Подготовленные массив аргументов для вызова конструктора</param>
/// <returns>true - если конструктор можно вызвать с переданными аргументами</returns>
public bool IsMatch(object[] args, out object[] parameters)
{
parameters = null;
int arg_index = 0;
if (Parameters.Count > 0)
{
parameters = new object[Parameters.Count];
for (int i = 0; i < parameters.Length; i++)
{
switch (Parameters[i].ParameterKind)
{
case ConstructorParameterKind.Parameter:
parameters[i] = _parent.Get(Parameters[i].ParameterResolveType, Parameters[i].ParameterResolveName);
break;
case ConstructorParameterKind.Resolve:
parameters[i] = _parent.Resolve(Parameters[i].ParameterResolveType, Parameters[i].ParameterResolveName);
break;
default:
if (args == null || arg_index >= args.Length) return false;
if (null == args[arg_index])
{
if (Parameters[i].IsNullable)
{
parameters[i] = args[arg_index];
}
else
{
return false;
}
}
else if (Parameters[i].Type.IsAssignableFrom(args[arg_index].GetType()))
{
parameters[i] = args[arg_index];
}
else
{
try
{
parameters[i] = Convert.ChangeType(args[i], Parameters[i].Type);
}
catch
{
return false;
}
}
arg_index++;
break;
}
}
return true;
}
if (args != null && args.Length > 0)
return false;
return true;
}
}
}

Powered by TurnKey Linux.