如何使用反射在泛型类型中调用非泛型方法
How to call non-generic methods inside generic types using reflection
使用 .Net Framework 4.8。
我正在为我的 MDI WinForms 应用程序创建一个快捷方式系统,这样当您在某些表单上按下某些键时,您可以使用自定义属性调用方法。
对于上下文,属性如下所示,并将它们保存为快捷方式:
[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);
}
}
我这样解析所有快捷方式并将它们保存为 Dictionary:
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
}
}
总而言之,它是这样工作的:
- 将 ShortcutTypeAttribute 添加到包含您要调用的方法的类型。
- 将ShortcutMethodAttribute添加到要调用的方法中(使用组合键)。
- 调用 RegisterAllAssemblyShortcuts()
- 确定活动 MDI 窗体的类型。
- 侦听键盘输入并检查快捷方式[mdiType] 是否匹配。
- 如果有 ShortcutEntry 则赋值对象并调用 ShortcutEntry.Trigger()。
所有这些步骤都可以正常工作
当我尝试使用在泛型类型上声明的 ShortcutEntry.Trigger() 调用非泛型方法时出现问题,如下所示:
[ShortcutTypeAttribute]
public class KeyboundForm<T> : Form where T : class
{
[ShortcutMethodAttribute(Keys.O)]
public virtual void KeyOPressed() {}
}
我得到的异常是:
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()
如何在泛型类型中调用非泛型方法?
答案编辑:现在可以使用了
替换了通用的触发函数以重新计算 methodinfo。
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
是在通用类型中声明的。您需要创建绑定泛型类型(即 KeyboundForm<SomeActualForm>
)才能调用它。
一种方法是更改反射以仅支持绑定泛型类型:
var typesWithAttribute = types
.Where(t => !t.ContainsGenericParameters)
.Where(x => x.GetCustomAttributes<ShortcutTypeAttribute>(false).Any())
这将捕获 non-generic 类型,如 SomeClass
并绑定泛型类型,如 SomeOtherClass : KeyboundForm<SomeFormType>
,并标有相应的属性。
或检查 类 的继承属性 (GetCustomAttributes<ShortcutTypeAttribute>(true)
),它们是绑定的泛型类型 (Type.IsConstructedGenericType == true
)。
相关:
- Invoke a non generic method with generic arguments defined in a generic class
使用 .Net Framework 4.8。
我正在为我的 MDI WinForms 应用程序创建一个快捷方式系统,这样当您在某些表单上按下某些键时,您可以使用自定义属性调用方法。
对于上下文,属性如下所示,并将它们保存为快捷方式:
[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);
}
}
我这样解析所有快捷方式并将它们保存为 Dictionary
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
}
}
总而言之,它是这样工作的:
- 将 ShortcutTypeAttribute 添加到包含您要调用的方法的类型。
- 将ShortcutMethodAttribute添加到要调用的方法中(使用组合键)。
- 调用 RegisterAllAssemblyShortcuts()
- 确定活动 MDI 窗体的类型。
- 侦听键盘输入并检查快捷方式[mdiType] 是否匹配。
- 如果有 ShortcutEntry 则赋值对象并调用 ShortcutEntry.Trigger()。
所有这些步骤都可以正常工作
当我尝试使用在泛型类型上声明的 ShortcutEntry.Trigger() 调用非泛型方法时出现问题,如下所示:
[ShortcutTypeAttribute]
public class KeyboundForm<T> : Form where T : class
{
[ShortcutMethodAttribute(Keys.O)]
public virtual void KeyOPressed() {}
}
我得到的异常是:
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()
如何在泛型类型中调用非泛型方法?
答案编辑:现在可以使用了
替换了通用的触发函数以重新计算 methodinfo。
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
forKeyOPressed() has
MethodInfo.ContainsGenericParameters == true`, when ...
这是因为 KeyOPressed
是在通用类型中声明的。您需要创建绑定泛型类型(即 KeyboundForm<SomeActualForm>
)才能调用它。
一种方法是更改反射以仅支持绑定泛型类型:
var typesWithAttribute = types
.Where(t => !t.ContainsGenericParameters)
.Where(x => x.GetCustomAttributes<ShortcutTypeAttribute>(false).Any())
这将捕获 non-generic 类型,如 SomeClass
并绑定泛型类型,如 SomeOtherClass : KeyboundForm<SomeFormType>
,并标有相应的属性。
或检查 类 的继承属性 (GetCustomAttributes<ShortcutTypeAttribute>(true)
),它们是绑定的泛型类型 (Type.IsConstructedGenericType == true
)。
相关:
- Invoke a non generic method with generic arguments defined in a generic class