简体   繁体   English

按依赖项排序.NET程序集

[英]Sort .NET Assemblies by dependencies

I have a set of .NET assemblies within my project. 我的项目中有一组.NET程序集。 I wish to sort them by their dependencies. 我希望按他们的依赖关系对它们进行排序。

So if I have (for example): 如果我有(例如):

IEnumerable<Assembly> unsorted = LoadAssembliesFromFolder();

I want to be able to call: 我希望能够打电话:

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

In reality, the resultant set would hopefully end up looking like the project build order dialog in Visual Studio. 实际上,结果集有望最终看起来像Visual Studio中的项目构建顺序对话框。

Any thoughts? 有什么想法吗? I don't really want to go down an iterative approach which could take quite some time. 我真的不想采用可能需要相当长时间的迭代方法。

Cheers 干杯

You will need to levarage the GetReferencedAssemblies() method of Assembly . 您将需要levrage AssemblyGetReferencedAssemblies()方法。 This returns a list of AssemblyName values and allows you to load the next assembly. 这将返回AssemblyName值列表,并允许您加载下一个程序集。 It's terribly inefficient, and will ensure that every assembly is loaded into memory, but it's something :-D 它非常低效,并且会确保每个程序集都加载到内存中,但它是:-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; }
    }
}

To use: 使用:

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

I found @Steven's answer too slow so I have come up with the following: 我发现@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);
}

It uses the TSort extension method from: 它使用TSort扩展方法:

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

For more information on how this works see the following article on Wikipedia: 有关其工作原理的更多信息,请参阅Wikipedia上的以下文章:

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

I solved this like : 我解决了这个问题:

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;
    }
}

Use it : 用它 :

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM