首页 > 解决方案 > 反射 - 收集 WinForm 控件

问题描述

描述:我想从许多程序集中的 1000 多个 Winform 中获取所有可用的控件。我不想进行大量的重构工作。(小工具,原因是内部使用)

已经完成的事情:得到所有程序集,并从每个程序集中通过type.IsSubclassOf(selectedType).

主要项目的信息:许多类都有默认构造函数,有些没有,有些则引用其他基类。项目结构不是那么优化,因为它是一个开发超过 15 年的产品。

问题:我试图通过反射来进行所有控制,Activator.CreateInstance(type)但是对于许多我采取了未处理的异常或其他无法管理的基类异常的类。我在此过程中遇到的另一个问题是铸造。我也尝试过FormatterServices.GetUninitializedObject(type),但我没有找到如何或是否可以进行控制。

问题:是否可以不费吹灰之力地收集具有这种结构的 winForm 'Form' 控件?实现这一目标的不同方法是什么?

在此处输入图像描述 在此处输入图像描述

代码示例

static List<AssemblyName> _assemblyList=new List<AssemblyName>();
static List<Type> _typeList = new List<Type>();
static List<Control> _controlList = new List<Control>();

private static void getFormTypes()
{
    foreach (AssemblyName assemblyName in _assemblyList)
    {
        Assembly assembly = Assembly.Load(assemblyName);
        foreach (Type type in assembly.GetTypes())
        {
            if (type.IsSubclassOf(typeof(Form)))
            {
                _typeList.Add(type);
            }
        }
    }
}

private static void getAllControlsFromFormTypes()
{
    foreach (Type type in _typeList)
    {
        object instance = default;
        
        if (HasValidConstructor(type))
        {
            instance = Activator.CreateInstance(type);//Here I have unhandled exceptions from base classes during constructor invoking
            GetValidControls((Control)instance);
        }
    }
}


private static void GetValidControls(Control container)
{
    foreach (Control control in container.Controls)
    {
        GetValidControls(control);

        if (!string.IsNullOrEmpty(control.Name) && !string.IsNullOrEmpty(control.Text))
        {
            _controlList.Add(control);
        }
    }
}

public static bool HasValidConstructor(Type t) => t.IsValueType || !t.IsAbstract && t.GetConstructor(Type.EmptyTypes) != null;```

标签: c#winformsreflectionwindows-runtime

解决方案


当您在窗体中放置一个控件时,设计器会为该控件创建一个成员字段。如果您没有更改GenerateMember属性来更改此行为,或者没有使用创建没有成员变量的标签的数据表单向导,那么您可以使用这些成员字段获取所有控件,而无需实例化表单。

对于每个程序集,您无需以这种方式实例化即可获得表单和控件的列表:

IEnumerable<Type> GetFormsAndControlsTypes(Assembly assembly)
{
    var forms = assembly.GetTypes()
        .Where(type => typeof(Form).IsAssignableFrom(type));

    var controls = forms.SelectMany(
        form => form.GetFields(
            BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
            .Where(field => typeof(Control).IsAssignableFrom(field.FieldType))
            .Select(field => field.FieldType));

    return forms.Concat(controls).Distinct().ToList();
}

但是,通常此类任务看起来像代码分析而不是运行时任务。


推荐阅读