在设计时使用其全名查找类的类型

时间:2016-06-14 08:48:47

标签: c# .net winforms visual-studio windows-forms-designer

编辑我在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

是否有任何推荐的方法或最佳方法来做同样的事情?

3 个答案:

答案 0 :(得分:2)

您可以使用这些服务在设计时使用类型:

例如,您可以编写此类方法并向其传递合适的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();
}

我在TypeConverterUiTypeEditorT4模板和附加组件中使用了这些机制。它与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;
                }