C#委托类型 - (System.Delegate)

本文详细解释了在WinForms应用程序中使用委托时遇到的问题,特别是针对Control.Invoke方法的使用进行了探讨,并提供了正确的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义

表示委托,委托是一种数据结构,它引用静态方法或引用类实例及该类的实例方法。 Delegate 类是委托类型的基类。然而,只有系统和编译器可以显式地从 Delegate 类或 MulticastDelegate 类派生。此外,还不允许从委托类型派生新类型。Delegate 类不是委托类型,该类用于派生委托类型。

问题

该类型不支持 直接使用 deleagte 关键字赋值实例

如下

this.Invoke(delegate
                {
                    this.textBox1.Text = num.ToString();
                });

上述代码 为winform中窗体类的成员方法, 其不能通过编译 , 无法将 匿名方法 转换为类型“System.Delegate”,因为它不是委托类型

Google结果

The problem the user is seeing is that the Thread ctor accepts a specific delegate – the ThreadStart delegate. The C# compiler will check and make sure your anonymous method matches the signature of the ThreadStart delegate and, if so, produces the proper code under-the-covers to create the ThreadStart delegate for you.

But Control.Invoke is typed as accepting a “Delegate”. This means it can accept any delegate-derived type. The example above shows an anonymous method that has a void return type and takes no parameters. It’s possible to have a number of delegate-derived types that match that signature (such as MethodInvoker and ThreadStart – just as an example). Which specific delegate should the C# compiler use? There’s no way for it to infer the exact delegate type so the compiler complains with an error.

对于Thread.ctor()来说,由于接受的是一个ThreadStart委托,编译器便可以将匿名函数与ThreadStart委托类型匹配,最后能够正确编译。
而对于Control.Invoke()来说,任何的代理类型都是可接受的,也就是说ThreadStart和MethodInvoker都是可以接受的类型。这样编译器反而不知道应该用哪个代理去匹配匿名函数了,导致了编译错误的发生。

引用自http://www.cnblogs.com/yelaiju/archive/2010/09/16/1827691.html

解决方案

Invoke(new MethodInvoker(delegate { this.textBox1.Text = num.ToString();}));
Invoke(new MethodInvoker(() => { this.textBox1.Text = num.ToString();});

this.Invoke((ThreadStart)delegate
                {
                     this.textBox1.Text = num.ToString();
                });

转载于:https://www.cnblogs.com/wycm/p/5338906.html

<think>我们正在解决的是:通过反射访问委托方法的ReflectedType时引发的System.NotSupportedException异常。 异常信息:'((System.Reflection.RuntimeMethodInfo)((System.Delegate)MModel.CaptureData.GetData).Method).ReflectedType' 根据引用[2]:所有的委托类型都是从System.MulticastDelegate类隐式派生的。而System.MulticastDelegate继承自System.DelegateC#委托中的+、-、+=、-=运算符都编译成了System.Delegate的静态Combine和Remove方法。 在C#中,委托实际上是一个包含方法引用的对象。当我们通过反射获取委托的方法信息时,可能会遇到一些特殊情况。 异常原因分析: 当我们尝试通过反射获取一个委托所指向的方法的ReflectedType属性时,可能会抛出System.NotSupportedException。这是因为ReflectedType属性在委托方法上的使用受到限制。 具体来说,委托可能绑定到一个非公开的方法(例如,私有方法、编译器生成的方法等),或者该方法可能是由编译器生成的(如匿名函数、lambda表达式等)。在这些情况下,ReflectedType属性可能无法正常返回,因为反射机制无法提供该信息。 此外,根据引用[4]中提到的枚举器的实现原理,我们可以联想到在反射中,有些元数据信息可能并不完整或者不支持某些操作。 解决方案: 1. 避免直接访问委托方法的ReflectedType属性。我们可以通过其他方式获取方法所属的类型。 2. 使用Method.DeclaringType代替ReflectedType。因为对于委托所指向的方法,DeclaringType属性通常可以返回定义该方法的类型。 但是注意:ReflectedType和DeclaringType的区别: - DeclaringType:返回声明该方法的类型(即该方法定义所在的类型)。 - ReflectedType:返回通过反射获取该方法的类型(即当前反射对象所关联的类型)。在通过反射获取方法时,如果该方法是从基类继承的,那么ReflectedType返回的是当前反射对象(子类)的类型,而DeclaringType返回的是基类的类型。 然而,在委托的情况下,我们通常关心的是方法定义所在的类型(DeclaringType),而不是通过哪个类型反射得到的。因此,使用DeclaringType可能是更安全且可行的。 3. 如果DeclaringType也不可用,那么可能需要考虑其他设计,避免通过反射来获取委托方法的信息。 示例代码: 假设我们有一个委托实例`del`,我们想要获取其方法的信息: ```csharp Delegate del = ...; // 某个委托实例 MethodInfo method = del.Method; // 不要使用method.ReflectedType,因为它可能抛出NotSupportedException // 使用DeclaringType Type declaringType = method.DeclaringType; ``` 但是,如果委托指向的是一个编译器生成的方法(如lambda表达式),那么DeclaringType可能会返回一个编译器生成的类型(例如,一个嵌套的私有类)。这种情况下,你可能需要根据实际情况处理。 4. 如果上述方法仍然不能满足需求,可能需要重新考虑设计,避免在委托上使用反射来获取类型信息。 另外,根据引用[1]中提到的关于P/Invoke的错误处理,我们可以联想到在反射操作中也要注意异常处理。 因此,建议在访问这些反射属性时进行异常捕获: ```csharp Type type = null; try { type = method.ReflectedType; } catch (NotSupportedException) { // 处理异常,例如使用DeclaringType代替 type = method.DeclaringType; } ``` 但是,根据异常信息,我们明确知道访问ReflectedType会抛出NotSupportedException,所以最好直接避免使用ReflectedType。 总结: 在反射访问委托的方法时,不要使用MethodInfo的ReflectedType属性,而应该使用DeclaringType属性。如果DeclaringType也不能满足需求,则需要考虑委托指向的方法是否是编译器生成的,并可能需要调整设计。 注意:这个异常通常发生在委托指向的方法是动态生成(如表达式树编译)或匿名方法时。在这种情况下,反射信息可能不完整。 根据引用[3]中提到的属性设置,我们可以联想到在反射操作中也要注意元数据的完整性,但这里不直接相关。 因此,解决方案的核心是:使用DeclaringType替代ReflectedType。 如果用户的需求是获取定义方法的类型,那么DeclaringType就是正确的选择。 示例代码(完整): 假设我们有一个类`MyClass`,其中有一个方法`MyMethod`,我们创建一个指向该方法的委托,然后通过反射获取方法所属的类型。 ```csharp public class MyClass { public void MyMethod() { } } // 创建委托 Action action = new MyClass().MyMethod; // 获取委托的方法信息 MethodInfo method = action.Method; // 使用DeclaringType获取定义该方法的类型 Type declaringType = method.DeclaringType; // 应该是MyClass // 不要使用ReflectedType,因为它可能抛出异常 ``` 但是,如果委托指向的是一个lambda表达式: ```csharp Action lambda = () => Console.WriteLine("Hello"); MethodInfo lambdaMethod = lambda.Method; Type lambdaDeclaringType = lambdaMethod.DeclaringType; // 这将返回一个编译器生成的类 ``` 在这种情况下,lambdaDeclaringType将是一个编译器生成的类型,名称通常类似于`<>c`。 因此,如果用户期望得到的是编写lambda表达式的上下文类,那么DeclaringType并不能直接给出,因为lambda表达式是在编译器生成的类中有一个方法。这时可能需要通过其他方式(如闭包捕获的变量)来推断上下文,但这通常很复杂。 所以,如果用户需要获取委托方法的声明类型,并且委托指向的是普通方法(非匿名方法),那么DeclaringType是有效的。如果指向的是匿名方法,则可能无法通过反射获取到用户编写代码的原始类。 最后,如果用户确实需要获取原始上下文,可能需要改变设计,例如在创建委托时同时记录类型信息。 相关问题: 1.C#反射中,ReflectedType和DeclaringType有什么区别? 2. 如何通过反射获取lambda表达式委托的原始上下文类型? 3. 如何避免在反射访问委托方法信息时抛出NotSupportedException异常? 4. C#委托指向编译器生成的方法时,反射信息有哪些限制? 注意:根据用户的问题,我们只关注委托的ReflectedType访问异常,因此重点在于使用DeclaringType替代。 回答完毕。</think>### C# `System.NotSupportedException` 异常解析解决方案 #### 异常原因分析 当通过反射访问委托方法的 `ReflectedType` 属性时抛出 `System.NotSupportedException`,根本原因是**委托指向的方法可能来自动态生成的类型或编译器生成的方法**,这些特殊场景下反射机制无法提供完整的元数据支持[^2][^4]。具体原因包括: 1. **编译器生成的方法** 委托指向匿名方法、lambda 表达式或迭代器方法时(如 `MoveNext`),这些方法由编译器动态生成,`ReflectedType` 属性无法返回有效值[^4]。 ```csharp // 示例:lambda 表达式会生成编译器方法 Action lambda = () => Console.WriteLine("Error"); var method = lambda.Method; var reflectedType = method.ReflectedType; // 抛出 NotSupportedException ``` 2. **动态程序集的方法** 方法来自 `Reflection.Emit` 动态创建的程序集时,反射元数据不完整。 3. **委托的特殊性** 委托本质是 `System.MulticastDelegate` 的子类,其方法可能被编译器重写(如 `+=` 编译为 `Combine`),导致反射信息受限[^2]。 --- #### 解决方案 ##### 1. 使用 `DeclaringType` 替代 `ReflectedType`(推荐) **`DeclaringType` 返回方法定义所在的原始类型**,不受编译器生成方法的影响: ```csharp Delegate myDelegate = MModel.CaptureData.GetData; MethodInfo methodInfo = myDelegate.Method; // 替换 ReflectedType 为 DeclaringType Type validType = methodInfo.DeclaringType; // ✅ 正常获取类型 Console.WriteLine(validType.FullName); ``` - **适用场景**:普通类方法、静态方法等非动态生成的方法[^4]。 ##### 2. 检查方法来源并跳过动态方法 对动态生成的方法显式跳过反射操作: ```csharp if (!methodInfo.IsDynamic && !methodInfo.DeclaringType.IsCompilerGenerated()) { Type type = methodInfo.ReflectedType; // 安全访问 } else { // 处理动态方法的替代逻辑 } ``` 需自定义扩展方法 `IsCompilerGenerated()` 检测特性标记: ```csharp public static bool IsCompilerGenerated(this Type type) => type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Length > 0; ``` ##### 3. 避免对委托方法直接反射 通过委托的 `Target` 属性获取实例类型: ```csharp if (myDelegate.Target != null) { Type instanceType = myDelegate.Target.GetType(); // 获取实际对象类型 } else { // 处理静态方法委托 Type staticType = methodInfo.DeclaringType; } ``` ##### 4. 使用表达式树捕获元数据(高级) 通过表达式树在编译时固化方法信息: ```csharp Expression<Action> expr = () => MModel.CaptureData.GetData(); var methodCall = expr.Body as MethodCallExpression; Type declaringType = methodCall?.Method.DeclaringType; // 安全获取类型 ``` --- #### 关键区别:`ReflectedType` vs `DeclaringType` | 属性 | 返回值场景 | 动态方法支持 | 委托兼容性 | |------------------|-------------------------------|-------------|-----------| | `ReflectedType` | 当前反射上下文中的类型 | ❌ 不支持 | ❌ 易异常 | | `DeclaringType` | 方法原始定义的类型 | ✅ 支持 | ✅ 推荐使用 | > **设计建议**:在委托反射场景中优先使用 `DeclaringType`,它更稳定且能处理编译器生成的方法[^2][^4]。 --- #### 错误示例修正 原问题代码: ```csharp // 错误访问方式:尝试获取 ReflectedType var type = ((Delegate)MModel.CaptureData.GetData).Method.ReflectedType; ``` 修正后: ```csharp // 正确访问方式:使用 DeclaringType var type = MModel.CaptureData.GetData?.Method.DeclaringType; ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值