按惯例扫描和自动注册时速度最快的C#IoC容器

时间:2015-02-11 08:53:29

标签: c# performance inversion-of-control ioc-container

我有一个很老的解决方案(63个项目)使用Unity

Scan(scanner =>
        {
            scanner.AssemblyContaining<ServiceRegistry>();
            scanner.WithNamingConvention();

            scanner.ExcludeType<ModuleManager>();
        });

但我想加快加载应用程序,但我真的找不到任何基准测试,以显示其他框架是否更快扫描我的装配并自动注册所有类型。

我发现http://www.palmmedia.de/blog/2011/8/30/ioc-container-benchmark-performance-comparison显示了很多更快的框架,但是没有使用可靠的扫描,并且可能不容易通过集成扫描获得更快的性能,因为我认为它需要慢速反射?

2 个答案:

答案 0 :(得分:0)

通常,程序集是Loaded,然后在第一次访问它时,将检索类名列表。这个过程非常快,不需要大量反射。通过网络加载DLL所花费的时间更为重要。

但是即使某些IoC在这些基准测试中看起来很慢,我也向你保证,像MEF这样的“慢速”IoC框架(我使用它)真的很快找到,编写和重新组合。你不会注意到有数百课的任何滞后事件。

瓶颈可以是在循环中实例化数万个类时。在这种情况下,IoC实例化的方式至关重要,但如果您使用IoC来实例化10 000个对象,则会遇到设计问题,而不是性能问题。

此外,选择IoC FW更可能是一个功能和易用性问题,而不是性能问题,因为大多数IoC fw甚至适用于大型项目。

答案 1 :(得分:0)

自己创建自定义扫描实现并不是很困难。这是我们使用的一个简单的:

internal class CommonConventions
{
    public static void RegisterDefaultConventions(
        Action<Type, Type> registerMethod, 
        Assembly[] interfaceAssemblies, 
        Assembly[] implementationAssemblies, 
        Type[] excludeTypes,
        string excludeRegEx)
    {
        List<Type> interfaces = new List<Type>();

        foreach (var assembly in interfaceAssemblies)
            interfaces.AddRange(GetInterfaces(assembly));

        foreach (var interfaceType in interfaces)
        {
            if (!IsExcludedType(interfaceType, excludeTypes, excludeRegEx))
            {
                List<Type> implementations = new List<Type>();

                foreach (var assembly in implementationAssemblies)
                    implementations.AddRange(GetImplementationsOfInterface(assembly, interfaceType).Where(implementation => !IsExcludedType(implementation, excludeTypes, excludeRegEx)).ToArray());

                // Prefer the default name ITypeName = TypeName
                Type implementationType = implementations.Where(implementation => IsDefaultType(interfaceType, implementation)).FirstOrDefault();

                if (implementationType == null)
                {
                    // Fall back on ITypeName = ITypeNameAdapter
                    implementationType = implementations.Where(implementation => IsAdapterType(interfaceType, implementation)).FirstOrDefault();
                }

                if (implementationType != null)
                {
                    System.Diagnostics.Debug.WriteLine("Auto registration of {1} : {0}", interfaceType.Name, implementationType.Name);
                    registerMethod(interfaceType, implementationType);
                }
            }
        }
    }

    public static void RegisterAllImplementationsOfInterface(
        Action<Type, Type> registerMethod,
        Type[] interfaceTypes,
        Assembly[] implementationAssemblies,
        Type[] excludeTypes,
        string excludeRegEx)
    {
        foreach (var interfaceType in interfaceTypes)
        {
            List<Type> implementations = new List<Type>();

            foreach (var assembly in implementationAssemblies)
                implementations.AddRange(GetImplementationsOfInterface(assembly, interfaceType));

            foreach (var implementationType in implementations)
            {
                if (!IsExcludedType(implementationType, excludeTypes, excludeRegEx))
                {
                    System.Diagnostics.Debug.WriteLine("Auto registration of {1} : {0}", interfaceType.Name, implementationType.Name);
                    registerMethod(interfaceType, implementationType);
                }
            }
        }
    }


    private static bool IsExcludedType(Type type, Type[] excludeTypes, string excludeRegEx)
    {
        return IsExcludedType(type, excludeTypes) || IsExcludedType(type, excludeRegEx) || IsExcludedType(type);
    }

    private static bool IsExcludedType(Type type, Type[] excludeTypes)
    {
        return excludeTypes.Contains(type);
    }

    private static bool IsExcludedType(Type type, string excludeRegEx)
    {
        if (string.IsNullOrEmpty(excludeRegEx)) return false;
        return Regex.Match(type.Name, excludeRegEx, RegexOptions.Compiled).Success;
    }

    private static bool IsExcludedType(Type type)
    {
        return type.GetCustomAttributes(typeof(MvcSiteMapProvider.DI.ExcludeFromAutoRegistrationAttribute), false).Length > 0;  
    }

    private static bool IsDefaultType(Type interfaceType, Type implementationType)
    {
        return interfaceType.Name.Equals("I" + implementationType.Name);
    }

    private static bool IsAdapterType(Type interfaceType, Type implementationType)
    {
        return implementationType.Name.EndsWith("Adapter") &&
            interfaceType.Name.Equals("I" + implementationType.Name.Substring(0, implementationType.Name.Length - 7));
    }

    private static IEnumerable<Type> GetInterfaces(Assembly assembly)
    {
        return assembly.GetTypes().Where(t => t.IsInterface);
    }

    private static IEnumerable<Type> GetImplementationsOfInterface(Assembly assembly, Type interfaceType)
    {
        return assembly.GetTypes().Where(t =>
            !t.IsInterface &&
            !t.IsAbstract &&
            interfaceType.IsAssignableFrom(t) &&
            t.GetConstructors(BindingFlags.Public | BindingFlags.Instance)
                .Any(type => type.GetParameters().Select(p => p.ParameterType).All(p => (p.IsInterface || p.IsClass) && p != typeof(string))));
    }
}

您可以阅读有关每位公众成员here的内容的文档。

然后使用这些约定:

// Matching type name (I[TypeName] = [TypeName]) or matching type name + suffix Adapter (I[TypeName] = [TypeName]Adapter)
// and not decorated with the [ExcludeFromAutoRegistrationAttribute].
CommonConventions.RegisterDefaultConventions(
    (interfaceType, implementationType) => this.Container.RegisterType(
        interfaceType, implementationType, new ContainerControlledLifetimeManager()),
    new Assembly[] { siteMapProviderAssembly },
    allAssemblies,
    excludeTypes,
    string.Empty);

// Multiple implementations of strategy based extension points (and not decorated with [ExcludeFromAutoRegistrationAttribute]).
CommonConventions.RegisterAllImplementationsOfInterface(
    (interfaceType, implementationType) => this.Container.RegisterType(
        interfaceType, implementationType, implementationType.Name, 
        new ContainerControlledLifetimeManager()),
    multipleImplementationTypes,
    allAssemblies,
    excludeTypes,
    string.Empty);

上述内容不会解决您的问题,但旨在通过创建您自己的约定为您指明正确的方向。

您最有可能看到的瓶颈是通过64个带反射的不同程序集,以便找到与特定界面匹配的所有类型。实际上,实现仅发生在几个(或一个)组件中。我建议您使用自定义属性来缩小此选择过程,以便仅扫描每个接口的相关(实现)程序集。

[AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
public class AutoRegistrationIncludeAssembliesAttribute
    : Attribute
{
    public AutoRegistrationIncludeAssembliesAttribute(params string[] assemblies)
    {
        this.Assemblies = assemblies;
    }

    public string[] Assemblies { get; private set; }
}

然后在每个接口中添加仅包含您将在其中找到实现的程序集名称的属性。

[AutoRegistrationIncludeAssemblies("Assembly1", "Assembly2")]
public interface ISomeInterface
{
}

在您的自定义约定中,过滤为实现而扫描的程序集。

var includeAssemblies = (AutoRegistrationIncludeAssembliesAttribute)interfaceType.GetCustomAttributes(typeof(AutoRegistrationIncludeAssembliesAttribute), false).FirstOrDefault();
if (includeAssemblies != null)
{
    var filteredImplementationAssemblies = implementationAssemblies.Where(a => includeAssemblies.Assemblies.Contains(a.FullName));
}

如果您希望扫描项目中的未知程序集,以防您无法控制接口可能位于的所有可能位置,则可能需要翻转并生成AutoRegistrationExcludeAssembliesAttribute实现。

另一件可以真正加快速度的事情是使用约定作为MSBuild过程的一部分,生成一个代码文件(DI模块),该文件包含基于编译步骤之前的约定的所有显式注册。这样,您仍然只需要维护约定,但在运行期间,所有类型都将被显式注册,因此在应用程序启动期间不会使用Reflection。