【问题标题】:How to prevent an assembly from loading when not used如何防止在不使用时加载程序集
【发布时间】:2013-10-31 13:32:53
【问题描述】:

我在 Winforms 中工作,C# .NET4.5

我在程序集A中有一个名为classA的类,它提供了method1

在我的主程序集中我可以调用这个方法,但在某些情况下我 100% 确定 由于注册表项的某些内部标志,这不可能发生:

if( valueFromRegistryIsAlwaysFalse)
{
   var A = new classA();
   A.method1();
}

当我在注册表中设置这个值时,我不想为程序集 A 提供主可执行文件,但是即使我不使用它需要程序集 A 启动的方法。我当然明白了,但是有什么办法可以解决这个问题吗?

【问题讨论】:

  • 澄清一下;您有一个可能存在或不存在的程序集,以及一个应指示您是否需要使用其中的类的注册表设置,但您希望允许应用程序运行而不尝试加载该程序集(如果类)不需要吗?
  • 奇怪...IME JIT 不会加载程序集,直到代码需要程序集中的某些内容。你是说那没有发生。即使没有使用任何代码,您是否也进行过测试以验证程序集是否正在加载?您是否也验证过在此之前没有其他代码需要程序集 A?
  • @P.Brian.Mackey 它需要程序集来 JIT 方法;避免这种情况通常是一个非常简单的重构
  • 我们的应用程序有两个版本:一个是控制硬件,另一个是除了硬件之外的所有功能。控制所有硬件的程序集在整个主应用程序中都使用,重构它是一项艰巨的工作(我不会被允许这样做)......我测试了这个

标签: c# .net winforms .net-assembly


【解决方案1】:

通常,当 CLI 对使用尚未加载的程序集的类型的任何包含方法进行 JIT 处理时,程序集加载和融合会按需发生。所以:“解决”这个问题的方法是将所有涉及此程序集中类型的代码移动到永远不会执行的方法中。例如:

if( valueFromRegistryIsAlwaysFalse)
{
   DoStuff();
}

[System.Runtime.CompilerServices.MethodImpl(
    System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
void DoStuff()
{
   var A = new classA();
   A.method1();
}

有了这个改变,原来的方法没有提到classA,所以方法可以JITted而不需要额外的汇编;并且因为DoStuff 从未被执行/ JITted,它也永远不会从那里加载。

【讨论】:

  • 谢谢,这正是我想要的!这种记录在案的行为是否也适用于未来的 .NET 框架?还是我现在要求太多了?
  • @Enrico 我不可能评论在未来的 .NET 框架中哪些工作/不工作 - 我在上次搬家时丢失了我的水晶球。
  • 我知道我问的太多了,但我可以试试!!谢谢!
【解决方案2】:

创建调用者和classA都引用的接口程序集。 删除引用并动态加载程序集并实例化类。

Assembly asm = Assembly.LoadFile(@"C:\classAAssembly.dll");
Type type = asm.GetType("classA");
IClassA a = Activator.CreateInstance(type) as IClassA;

【讨论】:

    【解决方案3】:

    我认为对于您的特殊情况,您也可以使用 MEF http://msdn.microsoft.com/en-us/library/dd460648.aspx

    【讨论】:

    • 添加对另一个库的引用以防止加载第一个库? :)
    • :) 对。也许不是最好的方法,但可以让你完全不包含对主项目的引用
    【解决方案4】:

    通常框架会按需加载程序集。另一种方法是使用动态负载模式或复合模式。从框架 4.0 开始,MS 引入了System.Addin 命名空间来帮助程序员。我在我的项目中使用了以下类来实现与 Framework 2.0 兼容性的相同:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Reflection;
    
    namespace MyNamespace
    {
        [Serializable]
        public enum CompositeLoaderFilter
        {
            ImplementsInterface = 0,
            InheritsBaseClass = 1
        }
    
        [Serializable]
        public static class Composite
        {
            private static readonly CompositeManager manager = new CompositeManager();
    
            public static CompositeManager Manager { get { return manager; } }
        }
    
        [Serializable]
        public class CompositeManager : MarshalByRefObject
        {
            private SortedList<string, Type> m_addIns;
    
            public int GetInMemoryComponents(Type addInType, CompositeLoaderFilter filter)
            {
                m_addIns = internal_GetInMemoryServices(addInType, filter);
                return m_addIns.Count;
            }
    
            public int GetComponents(Type addInType, CompositeLoaderFilter filter)
            {
                string addInPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                return GetComponents(addInPath, "*.dll", SearchOption.TopDirectoryOnly, addInType, filter);
            }
    
            public int GetComponents(string addInSearchPattern, Type addInType, CompositeLoaderFilter filter)
            {
                string addInPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                return GetComponents(addInPath, addInSearchPattern, SearchOption.TopDirectoryOnly, addInType, filter);
            }
    
            public int GetComponents(string addInPath, string addInSearchPattern, Type addInType, CompositeLoaderFilter filter)
            {
                return GetComponents(addInPath, addInSearchPattern, SearchOption.TopDirectoryOnly, addInType, filter);
            }
    
            public int GetComponents(string addInPath, string addInSearchPattern, SearchOption addInSearchOption, Type addInType, CompositeLoaderFilter filter)
            {
                AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
                setup.PrivateBinPath = addInPath;
                setup.ShadowCopyFiles = "false";
    
                AppDomain m_appDomain = AppDomain.CreateDomain("MyNamespace.CompositeManager", null, setup);
                CompositeManager m_remoteLoader = (CompositeManager)m_appDomain.CreateInstanceFromAndUnwrap(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "MyAssembly.dll"), "MyNamespace.CompositeManager");
                m_addIns = m_remoteLoader.RemoteGetServices(addInPath, addInSearchPattern, addInSearchOption, addInType, filter);
    
        #if DEBUG
                DebugLoadedAssemblies();
        #endif
    
                AppDomain.Unload(m_appDomain);
                return m_addIns.Count;
            }
    
            public object CreateInstance(string typeName)
            {
                if (!m_addIns.ContainsKey(typeName))
                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Type {0} was not loaded..", typeName), "typeName");
    
                MethodInfo method = m_addIns[typeName].GetMethod("GetInstance", BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
                if (method != null)
                    return method.Invoke(m_addIns[typeName], null);
                else return Activator.CreateInstance(m_addIns[typeName]);
            }
    
            public object CreateInstance(Type type)
            {
                if (type == null)
                    throw new ArgumentNullException("type", "Type is null");
    
                if (!m_addIns.ContainsKey(type.FullName))
                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Type {0} was not loaded..", type.FullName), "type");
    
                MethodInfo method = type.GetMethod("GetInstance", BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
                if (method != null)
                    return method.Invoke(type, null);
                else return Activator.CreateInstance(type);
            }
    
            public T CreateInstance<T>()
            {
                if (!m_addIns.ContainsKey(typeof(T).FullName))
                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Type {0} was not loaded..", typeof(T).FullName), "T");
    
                MethodInfo method = typeof(T).GetMethod("GetInstance", BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
                if (method != null)
                    return (T)method.Invoke(typeof(T), null);
                else return Activator.CreateInstance<T>();
            }
    
            public IEnumerable<Type> AvailableServices
            {
                get
                {
                    foreach (KeyValuePair<string, Type> item in m_addIns)
                    {
                        yield return item.Value;
                    }
                }
            }
    
            public Type[] AvailableTypes
            {
                get
                {
                    List<Type> list = new List<Type>();
                    foreach (KeyValuePair<string, Type> item in m_addIns)
                        list.Add(item.Value);
                    return list.ToArray();
                }
            }
    
            public T[] GetObjects<T>()
            {
                List<T> list = new List<T>();
                foreach (KeyValuePair<string, Type> item in m_addIns)
                    list.Add((T)CreateInstance(item.Value));
                return list.ToArray();
            }
    
            public object[] AvailableObjects
            {
                get
                {
                    List<object> list = new List<object>();
                    foreach (KeyValuePair<string, Type> item in m_addIns)
                        list.Add(CreateInstance(item.Value));
                    return list.ToArray();
                }
            }
    
            internal SortedList<string, Type> internal_GetInMemoryServices(Type addInType, CompositeLoaderFilter filter)
            {
                SortedList<string, Type> validAddIns = new SortedList<string, Type>();
                Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
                foreach (Assembly assembly in assemblies)
                {
                    try
                    {
                        Type[] types = assembly.GetTypes();
                        foreach (Type type in types)
                        {
                            switch (filter)
                            {
                                case CompositeLoaderFilter.ImplementsInterface:
                                if (type.GetInterface(addInType.Name) != null)
                                    validAddIns.Add(type.FullName, type);
                                break;
    
                                case CompositeLoaderFilter.InheritsBaseClass:
                                if (type.BaseType == addInType)
                                    validAddIns.Add(type.FullName, type);
                                break;
                            }
                        }
                    }
                    catch (FileLoadException flex)
                    {
                        Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "{0} MyNamespace.CompositeManager: {1}", DateTime.Now.ToString(), flex.Message));
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "{0} MyNamespace.CompositeManager: {1}", DateTime.Now.ToString(), ex.Message));
                    }
                }
                return validAddIns;
            }
    
            internal SortedList<string, Type> RemoteGetServices(string addInPath, string addInSearchPattern, SearchOption addInSearchOption, Type addInType, CompositeLoaderFilter filter)
            {
                string[] files = Directory.GetFiles(addInPath, addInSearchPattern, addInSearchOption);
                SortedList<string, Type> validAddIns = new SortedList<string, Type>();
    
                if (files.Length > 0)
                {
                    foreach (string file in files)
                    {
                        if (String.CompareOrdinal(addInPath, file) != 0)
                        {
                            try
                            {
                                Assembly assembly = Assembly.LoadFrom(file);
                                Type[] types = assembly.GetTypes();
                                foreach (Type type in types)
                                {
                                    switch (filter)
                                    {
                                        case CompositeLoaderFilter.ImplementsInterface:
                                        if (type.GetInterface(addInType.Name) != null)
                                            validAddIns.Add(type.FullName, type);
                                        break;
    
                                        case CompositeLoaderFilter.InheritsBaseClass:
                                        if (type.BaseType == addInType)
                                            validAddIns.Add(type.FullName, type);
                                        break;
                                    }
                                }
                            }
                            catch (FileLoadException flex)
                            {
                                Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "{0} MyNamespace.CompositeManager: {1}", DateTime.Now.ToString(), flex.Message));
                            }
                            catch (Exception ex)
                            {
                                Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "{0} MyNamespace.CompositeManager: {1}", DateTime.Now.ToString(), ex.Message));
                            }
                        }
                    }
                }
    
        #if DEBUG
                DebugLoadedAssemblies();
        #endif
    
                return validAddIns;
            }
    
        #if DEBUG
            internal void DebugLoadedAssemblies()
            {
                foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
                {
                    //Debug.WriteLine(string.Format("Domain: {0} Assembly: {1}", AppDomain.CurrentDomain.FriendlyName, a.FullName));
                }
            }
        #endif
        }
    }
    

    使用示例:

    Composite.Manager.GetComponents(typeof(IMyService), CompositeLoaderFilter.ImplementsInterface);
    IMyService[] services = Composite.Manager.GetObjects<IMyService>();
    

    该类将加载当前文件夹中的所有程序集,检查它们是否包含与输入参数匹配的类型。因为一旦加载了一个程序集就无法卸载,该类会将它们加载到不同的 AppDomain 中,这与单个程序集相反,可以删除。

    为了在您的项目中使用它,您应该按照以下步骤进行:

    • 删除对程序集的(硬)引用
    • 实现一个接口以与您的类进行通信
    • 加载实现上述接口的类型
    • 通过界面使用你的类

    希望对你有帮助:)

    【讨论】:

      猜你喜欢
      • 2010-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-19
      • 2012-02-16
      • 1970-01-01
      • 2023-04-09
      • 1970-01-01
      相关资源
      最近更新 更多