C# 按约定扫描和自动注册时最快的C IoC容器

C# 按约定扫描和自动注册时最快的C IoC容器,c#,performance,inversion-of-control,ioc-container,C#,Performance,Inversion Of Control,Ioc Container,我有一个大的解决方案63个项目,使用Unity Scan(scanner => { scanner.AssemblyContaining<ServiceRegistry>(); scanner.WithNamingConvention(); scanner.ExcludeType<ModuleManager>(); }); 但我想加快加载应用程序的速度,但我真

我有一个大的解决方案63个项目,使用Unity

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

            scanner.ExcludeType<ModuleManager>();
        });
但我想加快加载应用程序的速度,但我真的找不到任何基准测试来显示其他框架是否能够更快地扫描我的组件并自动注册所有类型


我发现这显示了许多更快的框架,但不是使用assemply扫描,而且使用assemply扫描可能不容易获得更快的性能,因为我认为它需要缓慢的反射?

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

但是,即使一些国际奥委会在这些基准上似乎进展缓慢,我向你们保证,像我使用的MEF这样缓慢的国际奥委会框架在发现、编写和重新编写东西方面确实很快。你不会注意到任何延迟,数百个类的事件

瓶颈可能是在循环中实例化成千上万个类时。在这种情况下,IoC实例化的方式非常关键,但如果使用IoC实例化10000个对象,则会出现设计问题,而不是性能问题


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

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

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))));
    }
}
上述内容无法解决您的问题,但旨在为您指明创建自己的约定的正确方向

您最可能看到的瓶颈是通过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));
}
如果希望扫描作为项目一部分的未知程序集,以防无法控制可能实现接口的所有潜在位置,则可能需要改变这一点,并创建自动注册ExcludeAsembliesAttribute


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

我们有一个大型WPF应用程序,它具有模块,我不知道有多少对象正在注册,但它的数量很多,如果没有IoC容器注册它们,你会建议我如何做才能让它更快呢?使用IoC,你不能比通过调用构造函数来实例化事物的正常方式更快。使用任何IoC都会带来性能开销。但是通过使用IoC,您只在需要时实例化所需的东西。因此,这两种方法之间有一种平衡。我真的认为你应该先看看使用不同国际奥委会的方式,然后做出选择。就我个人而言,我在有很多插件的大型项目上使用MEF,我从来没有遇到过性能问题。使用Unity扫描工具和自己迭代程序集并注册这些类型之间的性能差异是什么?对于一个包含64个项目的大规模解决方案,您可能已经达到了极限,尽管由于需要进行大量验证,组件注册也需要一些时间。有些容器可以更快地完成这项工作。另外,最好看看迭代程序集的开销是多少,看看切换容器是否有意义。
var includeAssemblies = (AutoRegistrationIncludeAssembliesAttribute)interfaceType.GetCustomAttributes(typeof(AutoRegistrationIncludeAssembliesAttribute), false).FirstOrDefault();
if (includeAssemblies != null)
{
    var filteredImplementationAssemblies = implementationAssemblies.Where(a => includeAssemblies.Assemblies.Contains(a.FullName));
}