反射以列出来自特定命名空间的 C# 应用程序中的返回类型和参数
我一直在开发一个 API,它封装了另一个使用起来比较棘手的 API。我的 API 的目标是不要求用户接触任何旧 API,方法是 1) 不需要旧 API 中类的任何参数,2) 不返回旧 API 中类的任何实例。是否有一个程序(也许是 Visual Studio 插件)可以分析我的 C# 解决方案,并为我提供可公开访问的类中可公开访问的方法的所有返回类型的列表,以及此类方法中的所有参数类型?否则,我似乎必须手动检查所有类,看看是否有任何旧 API 暴露给用户。
编辑:由于我一直使用 MSTest 对我的 API 进行单元测试,因此我添加了另一个单元测试来使用反射,如果旧 API 的任何部分暴露,则失败。但是,我遇到了反射问题。我在单元测试类中使用 OldAPI,然后使用它
AppDomain.CurrentDomain.GetAssemblies().SelectMany(
assembly => assembly.GetTypes()
)
来获取当前加载的所有程序集中的类型列表。然后,我迭代这些类型,希望将类型列表缩减为仅命名空间 OldAPI
中的类型。问题是命名空间 OldAPI
没有显示。我看到诸如 Microsoft.VisualStudio.TestTools、System.Reflection 之类的命名空间以及通过测试类中的 using
语句包含的其他命名空间,但没有“OldAPI”。这可能是因为旧 API 中存在 COM 内容,因此 AppDomain.CurrentDomain.GetAssemblies()
不包含该程序集,即使它是通过 using
语句包含在班级?
解决方案:通过任意选择一个我知道在OldAPI
中的类并执行以下操作,我获得了必要的程序集,这要归功于SLaks' 评论:
Func<Type, bool> isBad = t => t.Assembly == typeof(OldAPI.SomeClass).Assembly;
这是我的单元测试的片段,用于检查我的任何 API 类是否使用任何 OldAPI
类,感谢 SLaks' 答案:
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));
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));
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可以使用 LINQ,如下所示:
这在 LINQPad 中最容易实现。
请注意,这不会遍历泛型类型,因此它将忽略
List
。您可能应该使
isBad
递归。 (在这种情况下你应该把它变成一个常规函数)You can use LINQ, like this:
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)我不知道是否有现有的工具可以实现此目的,但这并不意味着您必须手动执行此操作 - 您可以非常轻松地编写自己的工具来使用反射来执行此操作。基本上你只需要迭代 Assembly.GetExportedTypes();对于每种类型,调用 Type.GetMethods() 和 Type.GetProperties() 并迭代结果;并转储每个公共方法或属性的返回和参数类型。
请注意,这样的手写工具需要运行已编译的程序集,而不是 C# 源代码。您可以执行类似于源代码的操作,但这取决于 Visual Studio 代码模型,该模型更难使用,并且可能不值得为这样的一次性工作付出努力!
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!