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

Posted

技术标签:

【中文标题】在设计时使用全名查找类的类型【英文标题】:Find the Type of a Class using its full name during Design-Time 【发布时间】:2016-10-14 21:37:06 【问题描述】:

编辑:我在 Visual Studio 的设计时使用DataSourceProviderService.InvokeAddNewDataSource 方法显示数据源配置向导。如果用户选择一个对象(as explained here),然后单击完成,我将得到一个类似"Namespace.ClassName" 的字符串。要在设计器中显示所选对象的属性,我需要以优化的方式找到对象的正确Type

我有一个类的名称及其命名空间 (Application.Data.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)

有没有推荐的方法或最好的方法来做同样的事情?

【问题讨论】:

Type.GetType(...)? this 可能有帮助,也许this 也有帮助 @MongZhu,我的代码也是如此,但这些不是解决这个问题的最佳或好方法 附带说明,如果两个具有完全相同的完全限定类型名称的类存在于两个单独的程序集中,那么它们在实际编程场景中将如何使用。如果您在程序中引用这两个程序集,编译器将永远无法区分它们 @RBT,在 Windows 窗体中,设计器中提供数据源向导用于数据绑定。使用它,我们可以在设计时选择任何类型作为数据源。我将它用于我的控件,向导返回一个带有命名空间和类名的字符串。我需要在设计时从字符串中识别正确的类型 【参考方案1】:

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

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();

我在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)));
    

【讨论】:

谢谢,我试试看 @Kira 结果如何,您能否确认解决方案有效?我可以在 Visual Studio 的设计器中使用代码,它工作正常。 请耐心等待,您的代码似乎已编辑。但我没有收到有关此的通知。我会检查并尽快通知您【参考方案2】:

正如您所说,您的算法中的搜索也在扫描不需要的程序集。如果您打算只搜索您自己产品的组件,那么您可以利用组件的标准命名法,以备不时之需。这将大大减少为目标类型扫描的目标程序集。 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;
                

【讨论】:

问题是数据源向导显示了所有可能程序集中的类名。数据源向导属于 VS,所以我也应该搜索系统程序集。如果我的控件的用户添加了太多程序集,我会看到我的代码需要很长时间 AppDomain.CurrentDomain.GetAssemblies() 在调试期间返回一个简单项目中包含 146 个程序集的数组。【参考方案3】:

内部循环相当于调用assembly.GetType(classNameWithNameSpace),所以可以完全跳过。这应该会处理您列表中的第 3 项。

可以通过确保Assembly 在 .NET 4.0 中没有 IsDynamic 标志,或检查 4.0 之前的命名空间来解决第 2 项。

此代码适用于 .NET 2.0

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);
    

在 .NET 4.0 之后用 LINQ 和 IsDynamic 重写:

var matchingTypes = AppDomain
    .CurrentDomain
    .GetAssemblies()
    .Where(a => !a.IsDynamic)
    .Select(a => a.GetType(classNameWithNameSpace))
    .Where(t => t != null)
    .ToList();

以上为您提供了带有classNameWithNameSpace 的所有类型的列表。

处理项目 #1 最好留给您的应用程序。您需要决定如何处理matchingTypes 列表中的每种类型。

记住type forwarding 很有用。上面的列表将包括这两种类型。您可以使用TypeForwardedToAttribute 来决定您实际应该采用哪种类型。

【讨论】:

3.5 框架支持 LINQ,框架 4 支持 IsDynamic。我也需要支持 2.0 框架。所以我在我的代码中使用了 for 循环 @Anand 你可能想在你的帖子中提到这一点:我不知道你必须支持.NET 2.0。 第 3 项是为了避免搜索不必要的程序集而不是内部 for 循环。设计时有 100 多个程序集 如果可以检查程序集中是否存在命名空间,我们可以避免不必要的搜索。因为命名空间的数量与 Type 相比非常少

以上是关于在设计时使用全名查找类的类型的主要内容,如果未能解决你的问题,请参考以下文章

Java的访问权限机制

获取嵌套类的全名

类的设计技巧简单总结

java之常用类的用法

图解算法之快速排序算法第一

2019-05-19 Jave学习日记之Object类型