c# - 如何使用反射调用泛型类型内的非泛型方法

标签 c# winforms generics reflection invoke

使用.Net Framework 4.8。

我正在为我的 MDI WinForms 应用程序创建一个快捷方式系统,以便您可以使用自定义属性在某些表单上按某些键时调用方法。

对于上下文,属性如下所示,并将它们保存为 Shortcutentry:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class ShortcutMethodAttribute : Attribute
{
    public Keys[] Combination {get; set;}
    public ShortcutMethodAttribute(params Keys[] combination)
    {
        Combination = combination;
    }
}

[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class)]
public sealed class ShortcutTypeAttribute : Attribute
{

}

public class ShortcutEntry
{
    public ShortcutMethodAttribute Attribute { get; private set; }
    public object Object { get; set; }
    public Keys[] KeyCombination { get; set; }
    public MethodInfo MethodInfo { get; private set; }

    public ShortcutEntry(object @object, Keys[] keyCombination, MethodInfo methodInfo, ShortcutMethodAttribute attrib)
    {
        this.Object = @object;
        this.KeyCombination = keyCombination;
        this.MethodInfo = methodInfo;
        this.Attribute = attrib;
    }

    public void Trigger()
    {
        MethodInfo.Invoke(Object, null);
    }
}

我像这样解析所有快捷方式并将它们保存为字典:

public Dictionary<Type, List<ShortcutEntry>> RegisterAllAssemblyShortcuts()
{
    var shortcuts = new Dictionary<Type, ShortcutEntry>();

    var types = Assembly.GetExecutingAssembly().GetTypes();
    var typesWithAttribute = types.Where(x => x.GetCustomAttributes<ShortcutTypeAttribute>(false).Any());

    foreach (var type in typesWithAttribute)
    {
        var methods = type.GetMethods().Where(x => x.GetCustomAttributes(typeof(ShortcutMethodAttribute), false).Length > 0);
        foreach (var method in methods)
        {
            var attributes = method.GetCustomAttributes(typeof(ShortcutMethodAttribute), false).OfType<ShortcutMethodAttribute>();
            if (attributes == null) continue;

            foreach (var attribute in attributes)
            {
                var se = new ShortcutEntry(
                    null,
                    attribute.KeyCombination,
                    method,
                    attribute
                    );
                if (!shortcuts.ContainsKey(type)) shortcuts.Add(type, new List<ShortcutEntry>);
                shortcuts[type].Add(se);
            }       
        }
    }
    return shortcuts;
}

要使用它,您需要将 ShortcutTypeAttribute 分配给一个类型,然后将 ShortcutMethodAttribute 分配给您要调用的方法,并将组合键作为参数传递。

[ShortcutTypeAttribute]
public class SomeClass
{
    public void SomeMethodA()
    {
        // do something
    }

    [ShortcutMethodAttribute(Keys.O, keys.I)]
    public void SomeMethodB()
    {
        // do something
    }
}

总而言之,它的工作原理如下:

  1. 将 ShortcutTypeAttribute 添加到包含您要调用的方法的类型。
  2. 将 ShortcutMethodAttribute 添加到要调用的方法(使用组合键)。
  3. 调用RegisterAllAssemblyShortcuts()
  4. 确定事件 MDI 表单的类型。
  5. 监听键盘输入并检查快捷方式[mdiType]是否匹配。
  6. 如果存在 ShortcutEntry,则分配该对象并调用 ShortcutEntry.Trigger()。

所有这些步骤都运行良好

当我尝试使用在泛型类型上声明的 ShortcutEntry.Trigger() 调用非泛型方法时,就会出现问题,如下所示:

[ShortcutTypeAttribute]
public class KeyboundForm<T> : Form where T : class
{
    [ShortcutMethodAttribute(Keys.O)]
    public virtual void KeyOPressed() {}
}

我得到的异常(exception)是:

System.InvalidOperationException: 'Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.'

我不知道为什么 KeyOPressed()MethodInfo 具有 MethodInfo.ContainsGenericParameters = true,当:

  • MethodInfo.IsGenericMethod = false
  • MethodInfo.IsGenericMethodDefinition = false

所以我无法在 KeyOPressed 的 MethodInfo 上调用 MakeGenericMethod()

如何在泛型类型中调用非泛型方法?

回答编辑:现在正在运行

替换了触发器函数以在方法信息为通用时重新计算方法信息。

public void Trigger()
{
    if (MethodInfo.ContainsGenericParameters)
    {
        var type = Object.GetType();
        var methodinfo = type.GetMethod(MethodInfo.Name);
        methodinfo.Invoke(Object, null);
    }
    else
    {
         MethodInfo.Invoke(Object, null);
    }
}

最佳答案

I don't know why the MethodInfo for KeyOPressed() has MethodInfo.ContainsGenericParameters == true`, when ...

这是因为KeyOPressed以泛型类型声明。您需要创建绑定(bind)泛型类型(即 KeyboundForm<SomeActualForm> )才能调用它。

一种方法是更改​​反射以仅支持绑定(bind)泛型类型:

var typesWithAttribute = types
    .Where(t => !t.ContainsGenericParameters)
    .Where(x => x.GetCustomAttributes<ShortcutTypeAttribute>(false).Any())

它将捕获非泛型类型,例如 SomeClass并绑定(bind)泛型类型,如 SomeOtherClass : KeyboundForm<SomeFormType>标有相应的属性。

或者检查绑定(bind)泛型类型 ( GetCustomAttributes<ShortcutTypeAttribute>(true) ) 的类的继承属性 ( Type.IsConstructedGenericType == true )。

相关:

关于c# - 如何使用反射调用泛型类型内的非泛型方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71982368/

相关文章:

c# - 我们可以使用对象访问私有(private)变量吗

c# - 我应该使用哪个 .NET 异常来指示外部应用程序失败?

c# - SQL 理论多引用

vb.net - 如何创建显示绑定(bind)到对象列表的百分比的条形图?

c# - 如何在 Windows 窗体 C# 的 DataGridView 中更改 'sort glyph icon' 颜色?

java - 如何创建具有更多限制类型的泛型类的子类?

c# - 帮助解决 C# 泛型错误 - "The type ' T' 必须是不可为空的值类型”

c# - DbContext 更新与 EntityState 修改

java - (Java) 如何使用有界通配符泛型实现接口(interface)方法?

vb.net - 无法将部分代码与按钮相关联