【问题标题】:Different behavior of reflected generic delegates with and without debugger带有和不带有调试器的反射泛型委托的不同行为
【发布时间】:2010-05-17 09:28:51
【问题描述】:

我们在调用反射泛型委托时遇到了一些奇怪的事情。在某些情况下,使用附加的调试器我们可以进行不可能的调用,而没有调试器我们无法捕获任何异常和应用程序快速失败。

代码如下:

using System;
using System.Windows.Forms;
using System.Reflection;

namespace GenericDelegate
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private delegate Class2 Delegate1();

        private void button1_Click(object sender, EventArgs e)
        {
            MethodInfo mi = typeof (Class1<>).GetMethod("GetClass", BindingFlags.NonPublic | BindingFlags.Static);
            if (mi != null)
            {
                Delegate1 del = (Delegate1) Delegate.CreateDelegate(typeof (Delegate1), mi);
                MessageBox.Show("1");
                try
                {
                    del();
                }
                catch (Exception)
                {
                    MessageBox.Show("No, I can`t catch it");
                }
                MessageBox.Show("2");
                mi.Invoke(null, new object[] {});//It's Ok, we'll get exception here
                MessageBox.Show("3");
            }
        }


        class Class2
        {

        }


        class Class1<T> : Class2
        {
            internal static Class2 GetClass()
            {
                Type type = typeof(T);
                MessageBox.Show("Type name " + type.FullName +" Type: " + type + " Assembly " + type.Assembly);
                return new Class1<T>();
            }
        }
    }
}

有两个问题:

  1. 调试器和不带调试器的行为不同
  2. 如果没有调试器,您无法通过 clr 技巧捕获此错误。这不是 clr 例外。有内存访问,读取内部代码内部的零指针。

用例: 您为您的应用程序开发类似插件系统的东西。您阅读外部程序集,找到某种类型的合适方法,然后执行它。我们只是忘记了我们需要检查的类型是泛型还是非泛型。在 VS(和 .net 从 2.0 到 4.0)下一切正常。被调用函数不使用泛型类型和类型参数的静态上下文。但是没有 VS 应用程序失败,没有声音。我们甚至无法识别调用堆栈附加调试器。

使用 .net 4.0 测试

问题是为什么 VS 捕获但运行时没有?

【问题讨论】:

    标签: c# .net reflection delegates generics


    【解决方案1】:

    嗯,是的,它的行为不太优雅。它在 .NET 4.0 中部分修复,CreateDelegate() 方法返回 null。但是,当启用 JIT 优化器时,这仍然不能正常运行,它假定 CreateDelegate 不能返回 null 并且不执行 null 检查。您将收到 FatalExecutionEngineError 而不是 NRE,这是 4.0 中不再可捕获的异常

    我建议您将缺陷报告给 connect.microsoft.com。不太确定他们是否会认真对待您,当您故意绕过保护措施时,我不知道他们的政策是什么。他们很可能认为 FEEE 足够好。解决方法很明显,只调用具体类型的方法。

    【讨论】:

      【解决方案2】:

      我不确定我是否完全按照您想要做的,但如果您将代码放在 try/catch 块中,肯定会引发异常。

      【讨论】:

      • 有些信息在某些情况下调用del();导致异常,例如“对象图不完整的对象”。但在我的机器上却没有。
      • 不幸的是,我无法在 4.0 上尝试它,但在 VS2005/2.0 上,如果您在对 Application.Run 语句的调用周围放置一个 try/catch,它会捕获异常。不过,这并不能真正解决您的问题。
      • 如果VS的调试器附加到进程函数“del();”被执行,而它不应该被执行。它应该失败,因为没有指定类型参数。如果没有附加调试器,它会在没有 clr 异常的情况下终止进程。
      • 好吧,我尝试了上面写的内容 - 在创建表单之前,在顶层进行了 try/catch。它肯定会捕获 System.AccessViolationException 类型的异常。
      • 没有。附加调试器后,我得到一个不同的异常:InvalidOperationException。
      【解决方案3】:

      刚刚在 VS 2008 下测试 VS 根本不会捕获异常。
      这是修改后的测试用例

      static class Program
          {
              private delegate Foo Delegate1();
              /// <summary>
              /// The main entry point for the application.
              /// </summary>
              [STAThread]
              static void Main()
              {
      
                  Application.EnableVisualStyles();
                  Application.SetCompatibleTextRenderingDefault(false);
                  MethodInfo mi = typeof(Bar<>).GetMethod("GetClass", BindingFlags.NonPublic | BindingFlags.Static);
                  if (mi != null)
                  {
                      var del = (Delegate1)Delegate.CreateDelegate(typeof(Delegate1), mi);
      
                      MessageBox.Show("1");
                      try
                      {
                          del();
                          //mi.Invoke(null, BindingFlags.NonPublic | BindingFlags.Static, null, null,CultureInfo.InvariantCulture);
                      }
                      catch (Exception)
                      {
                          MessageBox.Show("No, I can`t catch it");
                      }
                      MessageBox.Show("2");
                      mi.Invoke(null, new object[] { });//It's Ok, we'll get exception here
                      MessageBox.Show("3");
                  }
              }
              class Foo
              {
      
              }
              class Bar<T> : Foo
              {
                  internal static Foo GetClass()
                  {
                      Type type = typeof(T);
                      MessageBox.Show("Type name " + type.FullName + " Type: " + type + " Assembly " + type.Assembly);
                      return new Bar<T>();
                  }
              }
          }
      

      但是如果你评论 del() 和 uncomment mi.Invoke() 你会得到很好的例外
      真的。

      后期绑定操作不能 对类型或方法执行 其中 ContainsGenericParameters 是 真的

      【讨论】:

        【解决方案4】:

        我认为您发布的代码实际上无法在 .net 框架下运行。 2.0 到 4.0。静态方法只能在封闭的泛型类型上调用,而不是在打开的泛型类型上调用。所以你必须提供一个类型参数。至于调试器,请尝试在 Visual Studio 之外启动您的进程,然后附加 VS-Debugger。这次您将立即获得预期的异常。

        【讨论】:

        • >>“所以你必须提供一个类型参数”是的。我必须提供类型参数。我没有。我忘了。或者我什至不知道我处理的是泛型类型。并且代码有效。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多