Различное поведение отраженных обобщенных делегатов с отладчиком и без него - PullRequest
3 голосов
/ 17 мая 2010

Мы столкнулись с некоторыми странными вещами при вызове отраженных обобщенных делегатов.В некоторых случаях с привязанным отладчиком мы можем сделать невозможный вызов, в то время как без отладчика мы не можем перехватить любые исключения и fastfails приложения.

Вот код:

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>();
            }
        }
    }
}

Есть две проблемы:1006 *

  1. Поведение отличается с отладчиком и без
  2. Вы не можете поймать эту ошибку без отладчика с помощью трюков clr.Это просто не исключение.Есть доступ к памяти, чтение нулевого указателя внутри внутреннего кода.

Вариант использования: Вы разрабатываете что-то вроде системы плагинов для своего приложения.Вы читаете внешнюю сборку, находите подходящий метод в каком-то виде и выполняете его.И мы просто забыли о том, что нам нужно проверить, является ли тип универсальным или нет.Под VS (и .net от 2.0 до 4.0) все отлично работает.Вызываемая функция не использует статический контекст универсального типа и параметров типа.Но без VS приложение не работает без звука.Мы даже не можем идентифицировать отладчик, присоединяющийся к стеку вызовов.

Протестировано с .net 4.0

Вопрос в том, почему VS ловит, а среда выполнения не делает?

Ответы [ 4 ]

2 голосов
/ 17 мая 2010

Ну да, это не очень изящно.Это частично исправлено в .NET 4.0, метод CreateDelegate () возвращает ноль.Это все еще не ведет себя изящно, когда оптимизатор JIT включен, однако, он предполагает, что CreateDelegate не может вернуть ноль и не выполняет проверку на ноль.Вы получите FatalExecutionEngineError вместо NRE, исключение, которое больше не может быть перехвачено в 4.0

. Я бы порекомендовал сообщить об ошибке на connect.microsoft.com.Не уверен, что они воспримут вас всерьез, я не знаю, какова их политика, когда вы намеренно обходите защитные меры.Вполне может быть, что они считают FEEE достаточно хорошим.Обходной путь очевиден, вызывайте только методы конкретных типов.

1 голос
/ 18 мая 2010

Я не думаю, что код, который вы разместили, на самом деле работает в рамках .net. 2,0 до 4,0. Статические методы можно вызывать AFAIK только для закрытых универсальных типов, но не для открытых универсальных типов. Таким образом, вам придется предоставить параметр типа. Что касается отладчика, попробуйте запустить ваш процесс вне Visual Studio, а затем подключите VS-отладчик. На этот раз вы получите ожидаемое исключение сразу.

1 голос
/ 17 мая 2010

Только что протестировано под 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 (), вы получите хорошее исключение
правда.

Операции с поздним связыванием не могут быть выполняется на типах или методах для который содержит GenericParameters правда

1 голос
/ 17 мая 2010

Я не уверен, что точно следую тому, что вы хотите сделать, но если вы поместите свой код в блок try / catch, безусловно, будет сгенерировано исключение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...