编辑我在Visual Studio的设计时使用DataSourceProviderService.InvokeAddNewDataSource
方法显示数据源配置向导。如果用户选择了一个对象(as explained here),然后单击“完成”,我将得到一个类似"Namespace.ClassName"
的字符串。要在设计器中显示所选对象的属性,我需要以优化的方式找到对象的正确Type
。
我有一个类及其命名空间(Application.Data.Employee
)的名称。我想找到具有此信息的类(Employee)的类型。目前我使用以下代码来查找类型
string classNameWithNameSpace = "Application.Data.Employee";
Type target;
foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
foreach (Type t in assembly.GetTypes())
{
if (t.FullName.Equals(classNameWithNameSpace))
{
target = t;
break;
}
}
注意:程序中引用的任何dll中可能存在程序集。我的代码还支持 .Net Framework 2.0
我知道这不是最好的方法,原因如下
1)两个或多个程序集可能具有相同的命名空间名称和类名
2)我看到一篇SO帖子说明,它会为动态集会抛出NotSupportedException
3)在调试时发现在循环中检查了不需要或不必要的程序集中的类型。 AppDomain.CurrentDomain.GetAssemblies()
方法在设计时调试期间在一个简单项目中返回146个程序集
4)如果上面的代码将不必要的程序集加载到内存中,它将出现在内存中,直到存在应用程序域(请检查在此链接中卸载程序集部分https://msdn.microsoft.com/en-us/library/mt632258.aspx)
是否有任何推荐的方法或最佳方法来做同样的事情?
答案 0 :(得分:2)
您可以使用这些服务在设计时使用类型:
ITypeResolutionServic
可帮助您在设计时按名称检索装配或类型。ITypeDiscoveryService
可帮助您在设计时获取可用类型列表。例如,您可以编写此类方法并向其传递合适的IServiceProvider
,以获取这些服务:
Type GetTypeByName(IServiceProvider provider, string typeName)
{
var svc= (ITypeResolutionService)provider.GetService(typeof(ITypeResolutionService));
return svc.GetType(typeName);
}
private List<Type> GetAllTypes(IServiceProvider provider)
{
var svc= (ITypeDiscoveryService)provider.GetService(typeof(ITypeDiscoveryService));
return svc.GetTypes(typeof(object), true).Cast<Type>().ToList();
}
我在TypeConverter
,UiTypeEditor
,T4
模板和附加组件中使用了这些机制。它与visual studio在设计时用于处理类型的方式相同。
以下是获取属性所需的确切代码:
var svc = ((DataSourceProviderService)Site.GetService(typeof(DataSourceProviderService)));
if (svc != null)
{
var result = svc.InvokeAddNewDataSource(this, FormStartPosition.CenterScreen);
if(result!=null && result.DataSources.Count>0)
{
var type = GetTypeByName(this.Site, result.DataSources[0].TypeName);
var properties = type.GetProperties().ToList();
MessageBox.Show(string.Join(",", properties.Select(x => x.Name)));
}
}
答案 1 :(得分:1)
内部循环相当于调用assembly.GetType(classNameWithNameSpace)
,因此您可以完全跳过它。这应该从你的清单中处理第3项。
第2项可以通过确保{4.0}在.NET 4.0中没有Assembly
标志,或者在4.0之前检查命名空间来解决。
此代码适用于.NET 2.0
IsDynamic
在.NET 4.0之后用LINQ和IList<Type> matchingTypes = new List<Type>();
foreach(Assembly a in AppDomain.CurrentDomain.GetAssemblies()) {
// Skip dynamic assemblies.
if (a.GetType().StartsWith("System.Reflection.Emit.")) {
continue;
}
Type t = a.GetType(classNameWithNameSpace);
if (t != null) {
matchingTypes.Add(t);
}
}
重写:
IsDynamic
以上为您提供了var matchingTypes = AppDomain
.CurrentDomain
.GetAssemblies()
.Where(a => !a.IsDynamic)
.Select(a => a.GetType(classNameWithNameSpace))
.Where(t => t != null)
.ToList();
所有类型的列表。
处理第1项是最适合您的应用程序的。您需要决定如何处理classNameWithNameSpace
列表中的每种类型。
记住type forwarding很有用。上面的列表将包括这两种类型。您可以使用TypeForwardedToAttribute
来确定您应该采用的类型。
答案 2 :(得分:1)
正如您所说,算法中的搜索也会扫描不需要的程序集。如果您计划只搜索自己的产品组件,那么您可以利用组件的标准命名法,以备不时之用。这将大大减少针对目标类型扫描的目标组件。 Line#XYZ执行过滤相关程序集的初始任务,假设要搜索的所有程序集在其名称中都有一些标准前缀MyCompanyName.MyProductName
。此外,我已经用LINQ调用替换了大部分调用,这些调用在语法上更加清晰。
string classNameWithNameSpace = "Application.Data.Employee";
Type target;
var assemblyList = AppDomain.CurrentDomain.GetAssemblies();
//line # XYZ
var filteredAssembliesOfMyProduct =
assemblyList.Where(x => x.FullName.StartsWith("MyCompanyName.MyProductName"));
foreach (Assembly assembly in filteredAssembliesOfMyProduct)
if (assembly.GetTypes().Any(x => x.FullName == classNameWithNameSpace))
{
target = assembly.GetTypes().First(x => x.FullName == classNameWithNameSpace);
break;
}