【问题标题】:Sort .NET Assemblies by dependencies按依赖项对 .NET 程序集进行排序
【发布时间】:2013-08-30 03:16:28
【问题描述】:

我的项目中有一组 .NET 程序集。我希望按它们的依赖关系对它们进行排序。

如果我有(例如):

IEnumerable<Assembly> unsorted = LoadAssembliesFromFolder();

我希望能够打电话:

var IEnumerable<Assembly> sorted = unsorted.SortByDependency();

实际上,结果集希望最终看起来像 Visual Studio 中的项目构建顺序对话框。

有什么想法吗?我真的不想采用可能需要相当长的迭代方法。

干杯

【问题讨论】:

  • 我认为这里除了迭代之外没有其他方法。
  • 你需要算法吗?
  • 你想如何处理循环引用? (例如框架中的SystemSystem.Xml相互引用)
  • 好问题 - 我不需要对系统库进行排序,只需要对我自己的库进行排序,但我怀疑在我的库集中,循环引用仅存在于库的同一“级别”(即类似的依赖集)意味着两者之间的顺序并不重要。

标签: c# .net reflection


【解决方案1】:

我解决了这个问题:

public class AssemblyInfo
{
    public readonly Assembly Item;
    public readonly IList<AssemblyInfo> ReferencedAssemblies;

    public AssemblyInfo(Assembly item)
    {
        Item = item ?? throw new NullReferenceException("Item is null");
        ReferencedAssemblies = new List<AssemblyInfo>();
    }

    int Count()
    {
        return ReferencedAssemblies.Count;
    }

    public override string ToString()
    {
        return Item.FullName;
    }

    public IEnumerable<AssemblyInfo> OrderedDependencies()
    {
        List<AssemblyInfo> localOrdered = new List<AssemblyInfo>();
        foreach (AssemblyInfo item in ReferencedAssemblies.OrderBy(t => t.Count()))
        {
            IEnumerable<AssemblyInfo> temp = item.OrderedDependencies();
            localOrdered = localOrdered.Union<AssemblyInfo>(temp).ToList();
        }
        localOrdered.Add(this);
        return localOrdered;
    }

    public override bool Equals(object obj)
    {
        //Check whether any of the compared objects is null.
        if (Object.ReferenceEquals(obj, null))
        {
            return false;
        }

        //Check whether the compared objects reference the same data.
        if (Object.ReferenceEquals(this, obj))
        {
            return true;
        }
        return Item.FullName.Equals(((AssemblyInfo)obj).Item.FullName);
    }

    public override int GetHashCode()
    {
        //Get hash code for the Name field if it is not null.
        return Item.FullName.GetHashCode();
    }

    public static AssemblyInfo Parse(string assembliesPath, string assemblyName)
    {
        return Parse(assembliesPath, assemblyName, new Dictionary<string, Assembly>());
    }
    static AssemblyInfo Parse(string assembliesPath, string assemblyName, Dictionary<string, Assembly> loadedAssemblies)
    {
        string assemblyFullPath = Path.Combine(assembliesPath, assemblyName);
        if (!File.Exists(assemblyFullPath))
        {
            return null;
        }
        if (loadedAssemblies == null)
        {
            loadedAssemblies = new Dictionary<string, Assembly>();
        }
        if (!loadedAssemblies.ContainsKey(assemblyFullPath))
        {
            loadedAssemblies.Add(assemblyFullPath, Assembly.Load(File.ReadAllBytes(assemblyFullPath)));
        }

        Assembly a = loadedAssemblies[assemblyFullPath];
        AssemblyInfo ai = new AssemblyInfo(a);
        foreach (AssemblyName an in a.GetReferencedAssemblies())
        {
            AssemblyInfo d = Parse(assembliesPath, an.Name + ".dll", loadedAssemblies);
            if (d != null)
            {
                ai.ReferencedAssemblies.Add(d);
            }
        }
        return ai;
    }
}

使用它:

AssemblyInfo ai = AssemblyInfo.Parse("assembliesPath","yourassembly.dll");
IEnumerable<AssemblyInfo> sorted = ai.OrderedDependencies();
foreach (AssemblyInfo item in sorted)
{
    Console.WriteLine(item.Item.ManifestModule.ToString());
}

【讨论】:

    【解决方案2】:

    我发现@Steven 的回答太慢了,所以我想出了以下内容:

    public class AssemblyItem {
        public Assembly Item { get; set; }
        public IList<AssemblyItem> Dependencies { get; set; }
    
        public AssemblyItem(Assembly item) {
            Item = item;
            Dependencies = new List<AssemblyItem>();
        }
    }
    
    public static void Main() {
        // Get the assemblies
        var assemblyItems = BuildManager.GetReferencedAssemblies().Cast<Assembly>().OrderBy(a => a.FullName).Select(a => new AssemblyItem(a)).ToList();
    
        // Add the dependencies
        foreach (var item in assemblyItems) {
            foreach (var reference in item.Item.GetReferencedAssemblies()) {
                var dependency = assemblyItems.SingleOrDefault(i => i.Item.FullName == reference.FullName);
    
                if (dependency != null)
                    item.Dependencies.Add(dependency);
            }
        }
    
        // Sort the assemblies
        var sortedAssemblyItems = assemblyItems.TSort(i => i.Dependencies);
    }
    

    它使用 TSort 扩展方法来自:

    https://stackoverflow.com/a/11027096/155899

    有关其工作原理的更多信息,请参阅维基百科上的以下文章:

    http://en.wikipedia.org/wiki/Topological_sort

    【讨论】:

      【解决方案3】:

      您需要利用AssemblyGetReferencedAssemblies() 方法。这将返回AssemblyName 值的列表,并允许您加载下一个程序集。这是非常低效的,并且会确保每个程序集都加载到内存中,但它是一些东西 :-D

      class AssemblyReferenceComparison : IComparer<Assembly>
      {
          public int Compare(Assembly x, Assembly y)
          {
              if (x == y) return 0;
              if (GetReferencesAssemblies(x).Contains(y)) return -1;
              if (GetReferencesAssemblies(y).Contains(x)) return 1;
              return 0;
          }
          private static IEnumerable<Assembly> GetReferencesAssemblies(Assembly a)
          {
              var referencedAssemblies = new HashSet<Assembly>();
      
              FillReferencesAssemblies(a, referencedAssemblies);
      
              return referencedAssemblies;
          }
      
          private static void FillReferencesAssemblies(Assembly a, HashSet<Assembly> referencedAssemblies)
          {
              referencedAssemblies.Add(a);
      
              var directAssemblies = a.GetReferencedAssemblies()
                  .Select(name => Load(name))
                  .Where(asm => asm != null)
                  .Where(asm => !referencedAssemblies.Contains(asm))
                  .ToArray();
      
              foreach (var directAssembly in directAssemblies)
              {
                  FillReferencesAssemblies(directAssembly, referencedAssemblies);
              }
          }
      
          [DebuggerStepThrough]
          private static Assembly Load(AssemblyName name)
          {
              try { return Assembly.Load(name); }
              catch { return null; }
          }
      }
      

      使用方法:

      var assemblies = LoadAssembliesFromFolder()
          .OrderBy(a => a, new AssemblyReferenceComparison())
          .ThenBy(a => a.FullName);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-13
        • 1970-01-01
        • 1970-01-01
        • 2011-04-18
        相关资源
        最近更新 更多