Reputation: 31640
I've been working on an API that encapsulates another, trickier-to-use API. The goal is for my API to not require the user to touch any of the old API by 1) not requiring any parameters of classes in the old API and 2) not returning any instances of classes in the old API. Is there a program, perhaps a Visual Studio plugin, that can analyze my C# solution and give me a list of all the return types from publicly accessible methods in publicly accessible classes, as well as all the parameter types in such methods? Otherwise it seems like I'll have to manually go through all my classes and see if any of the old API is exposed to the user.
Edit: Since I've been using MSTest for unit testing my API anyway, I added another unit test to use reflection and Fail if any parts of the old API are exposed. However, I'm stuck with a reflection problem. I have using OldAPI
in the unit test class and then I use
AppDomain.CurrentDomain.GetAssemblies().SelectMany(
assembly => assembly.GetTypes()
)
to get a list of the types in all the assemblies currently loaded. I then iterate over those in hopes of paring down the list of types to only those in the namespace OldAPI
. The problem is that the namespace OldAPI
does not show up. I see namespaces like Microsoft.VisualStudio.TestTools, System.Reflection, and others that are included via using
statements in the test class, but no "OldAPI". Could this be because of COM stuff with the old API, so AppDomain.CurrentDomain.GetAssemblies()
doesn't include the assembly even though it's included via a using
statement in the class?
Solution: I got the necessary assembly by arbitrarily choosing one class I know is in OldAPI
and doing the following, thanks to SLaks' comment:
Func<Type, bool> isBad = t => t.Assembly == typeof(OldAPI.SomeClass).Assembly;
Here's a snippet of my unit test for checking if any of my API's classes use any of OldAPI
's classes, thanks to SLaks' answer:
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));
Upvotes: 1
Views: 764
Reputation: 887479
You can use LINQ, like this:
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));
This would be easiest to do in LINQPad.
Note that this doesn't traverse generic types, so it'll ignore a List<BadType>
.
You should probably make isBad
recursive. (In which case you should turn it into a regular function)
Upvotes: 1
Reputation: 74802
I'm not aware of an existing tool for this, but that doesn't mean you have to do it manually -- you can very easily write your own tool to do this using Reflection. Basically you'll just need to iterate over Assembly.GetExportedTypes(); for each type, call Type.GetMethods() and Type.GetProperties() and iterate over the results; and dumping the return and parameter types for each method or property that is public.
Note that such a handwritten tool would need to run over your compiled assembly, not your C# source code. You can do something similar to source code, but it depends on the Visual Studio code model which is rather harder to use and probably not worth the effort for a one-off like this!
Upvotes: 1