2010-01-06 5 views
0

J'ai travaillé sur une API qui encapsule une API plus complexe à utiliser. L'objectif est que mon API ne nécessite pas que l'utilisateur touche l'une des anciennes API en 1) ne nécessitant aucun paramètre de classes dans l'ancienne API et 2) ne renvoyant aucune instance de classes dans l'ancienne API. Existe-t-il un programme, peut-être un plugin Visual Studio, capable d'analyser ma solution C# et de me fournir une liste de tous les types de retour de méthodes accessibles au public dans des classes accessibles au public, ainsi que tous les types de paramètres dans ces méthodes? Sinon, il semble que je vais devoir passer en revue manuellement toutes mes classes et voir si l'une des anciennes API est exposée à l'utilisateur. Depuis que j'utilise MSTest pour tester mon API de toute façon, j'ai ajouté un autre test unitaire pour utiliser la réflexion et Fail si des parties de l'ancienne API sont exposées. Cependant, je suis coincé avec un problème de réflexion. Je using OldAPI dans la classe de test unitaire, puis-je utiliserRéflexion pour lister les types de retour, les paramètres dans l'application C# à partir d'un espace de noms spécifique

AppDomain.CurrentDomain.GetAssemblies().SelectMany(
    assembly => assembly.GetTypes() 
) 

pour obtenir une liste des types dans toutes les assemblées actuellement chargées. Je itère alors sur ceux dans l'espoir de réduire la liste des types à seulement ceux dans l'espace de noms OldAPI. Le problème est que l'espace de noms OldAPI n'apparaît pas. Je vois des espaces de noms comme Microsoft.VisualStudio.TestTools, System.Reflection, et d'autres qui sont inclus via les instructions using dans la classe de test, mais pas "OldAPI". Est-ce que cela peut être dû à des choses COM avec l'ancienne API, alors AppDomain.CurrentDomain.GetAssemblies() n'inclut pas l'assembly même s'il est inclus via une instruction using dans la classe?

Solution: J'obtenu l'assemblage nécessaire en choisissant arbitrairement une classe que je sais est OldAPI et de faire ce qui suit, grâce à SLaks 'commentaire:

Func<Type, bool> isBad = t => t.Assembly == typeof(OldAPI.SomeClass).Assembly; 

Voici un extrait de mon test unitaire pour le contrôle si l'une des classes de mon API utilisent l'une des OldAPI « s cours, grâce à SLaks » réponse:

MethodInfo[] badMethods = methods.Where(
    m => (
      isBad(m.ReturnType) || 
      m.GetParameters().Any(p => isBad(p.ParameterType)) 
     ) && !isBad(m.DeclaringType) 
).ToArray(); 
string[] badMethodNames = badMethods.Select(
    m => m.DeclaringType.Name + "." + m.Name 
).Distinct().ToArray(); 
Assert.AreEqual(0, badMethodNames.Length, "Some methods in " + 
    monitoredNamespaces + " projects expose OldAPI: " + 
    string.Join(", ", badMethodNames)); 
+1

Vous confondez des assemblys avec des espaces de noms. Vous pouvez créer l'assembly de l'ancienne API en écrivant 'typeof (OldType).Assemblée ». – SLaks

Répondre

1

Vous pouvez utiliser LINQ, comme ceci:

Func<Type, bool> isBad = t => t.Assembly == badAssembly; 

var types = yourAssembly.GetTypes(); 
var methods = types.SelectMany(t => t.GetMethods()).ToArray(); 

var badMethods = methods.Where(m => isBad(m.ReturnType) 
    || m.GetParameters().Any(p => isBad(p.ParameterType); 

var properties = types.SelectMany(t => t.GetProperties()).ToArray(); 
var badProperties = properties.Where(p => isBad(p.PropertyType)); 

Ce serait plus facile à faire dans LINQPad.

Notez que ceci ne traverse pas les types génériques, donc il ignorera un List<BadType>. Vous devez probablement faire isBad récursive. (Dans ce cas, vous devriez le transformer en une fonction régulière)

+0

Vous avez deux parenthèses mal placées: une supplémentaire à la fin de 'var methods' et une autre à la fin de' var badMethods'. –

+0

J'ai modifié le paramètre 'badMethods' à' Where' pour inclure '&&! IsBad (m.DeclaringType)', sinon je vois toutes les classes de la mauvaise API répertoriées car elles utilisent bien sûr d'autres classes dans l'API incorrecte. Je souhaite que seules mes propres classes utilisant l'API incorrecte soient répertoriées. –

+0

Je mets aussi 'ToArray()' à la fin de 'var badMethods' et' var badProperties' car 'Where' renvoie un' IEnumerable'. –

1

Je ne suis pas SACR e d'un outil existant pour cela, mais cela ne signifie pas que vous devez le faire manuellement - vous pouvez très facilement écrire votre propre outil pour le faire en utilisant Reflection. Fondamentalement, vous aurez juste besoin d'itérer sur Assembly.GetExportedTypes(); pour chaque type, appelez Type.GetMethods() et Type.GetProperties() et parcourez les résultats; et en vidant les types de retour et de paramètre pour chaque méthode ou propriété qui est publique. Notez qu'un tel outil manuscrit doit être exécuté sur votre assembly compilé, pas votre code source C#. Vous pouvez faire quelque chose de similaire au code source, mais cela dépend du modèle de code de Visual Studio qui est plus difficile à utiliser et qui ne vaut probablement pas la peine d'être utilisé pour un élément unique comme celui-ci!

+0

Vous m'avez peut-être donné l'idée d'écrire un projet Open Source pendant mon temps libre. :) –

Questions connexes