简体   繁体   中英

Find the Type of a Class using its full name during Design-Time

Edit: I am using DataSourceProviderService.InvokeAddNewDataSource method to display Data Source Configuration Wizard during design-time in Visual Studio. If user choose an object ( as explained here ), and click finish, I will get a string like "Namespace.ClassName" . To display the Properties of the selected object in designer, I need to find the correct Type of the object in an optimized manner.

I have the name of a class and its namespace ( Application.Data.Employee ). I want to find the type of the class (Employee) with this information. At present I am using the following code to find the type

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

Note : Assembly might be present in any dll referenced in the project. My code also supports .Net Framework 2.0

I know this is not the best way because of the following reasons

1) Two or more assemblies might have same namespace name and class name

2) I saw a SO post stating, it will throw NotSupportedException for dynamic assemblies

3) On debugging found that Types in unwanted or unnecessary assemblies are checked in the loop. AppDomain.CurrentDomain.GetAssemblies() method returns 146 assemblies in a simple project during design-time debugging

4) If above code loads an unnecessary assembly into memory, it will present in memory till application domain is present (check unloading an assembly section in this link https://msdn.microsoft.com/en-us/library/mt632258.aspx )

Is there any recommended way or best approach for doing the same?

You can use these services to work with types at design-time:

For example you can write such methods and pass them a suitable IServiceProvider which can get those service:

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

I've used these mechanism in TypeConverter , UiTypeEditor , T4 templates and Add-ons. It's the same way that visual studio uses to work with types at design-time.


Here is the exact code you need to get properties:

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

The inner loop is equivalent to calling assembly.GetType(classNameWithNameSpace) , so you can skip it completely. This should take care of item 3 from your list.

Item 2 can be solved by ensuring that Assembly does not have IsDynamic flag in .NET 4.0, or checking the namespace prior to 4.0.

This code is suitable for .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);
    }
}

Rewrite with LINQ and IsDynamic after .NET 4.0:

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

The above gives you a list of all types with classNameWithNameSpace .

Dealing with item #1 is something best left to your application. You need to decide what to do with each of the types on the matchingTypes list.

It is useful to remember about type forwarding . The list above will include both types. You can use TypeForwardedToAttribute to decide which type you should actually take.

As you said that the search in your algorithm is also scanning unwanted assemblies. In case you plan to search only your own product's assemblies then you can leverage the standard nomenclature of the assemblies in case you have it. This will dramatically reduce the targeted assemblies which are scanned for the target type. Line # XYZ does the initial task of filtering the relevant assemblies assuming all the assemblies to be searched have a some standard prefix MyCompanyName.MyProductName in their name. Also I've replaced most of your calls with LINQ calls which are syntactically lot more cleaner.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM