2013-08-27 3 views
4

У меня есть набор .NET-сборок в моем проекте. Я хочу сортировать их по их зависимостям.Сортировка .NET Assemblies по зависимостям

Так что, если у меня есть (например):

IEnumerable<Assembly> unsorted = LoadAssembliesFromFolder(); 

Я хочу, чтобы быть в состоянии назвать:

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

В действительности, полученный набор, мы надеемся, в конечном итоге выглядит как сборки проекта порядок заказа в Visual Studio.

Любые мысли? Я действительно не хочу останавливаться на итеративном подходе, который может занять довольно много времени.

Приветствия

+0

I не думайте, что здесь есть другой способ, чем итеративный. –

+0

Вам нужен алгоритм? –

+0

Почему downvote? – Kazar

ответ

3

Вы должны будете levarage в GetReferencedAssemblies() метод Assembly. Это возвращает список значений 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); 
0

Я нашел @ ответ Стивена слишком медленно, так что я придумал со следующим:

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

0

Я решил это, как:

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