Каково различие между вызовом делегата непосредственно, использованием DynamicInvoke и использованием DynamicInvokeImpl?

Это довольно распространенная проблема. Вы используете | DataDirectory | строка подстановки. Это означает, что при отладке вашего приложения в среде Visual Studio база данных, используемая вашим приложением, находится в папке подпапки BIN\DEBUG (или версии x86) вашего проекта. И это хорошо работает, так как у вас нет какой-либо ошибки, связанной с базой данных и выполняющей операции обновления.

Но затем вы выходите из сеанса отладки и просматриваете свою базу данных через Visual Studio Server Explorer. Это окно имеет другую строку подключения (возможно, указывая на копию вашей базы данных в папке проекта). Вы просматриваете свои таблицы, и вы не видите изменений.

Затем проблема ухудшается. Вы перезапускаете VS, чтобы искать ошибку в своем приложении, но у вас есть файл базы данных, указанный между вашими файлами проекта, а для свойства Copy to Output directory установлено значение Copy Always. На этом этапе Visual Studio копирует исходный файл базы данных из папки проекта в выходную папку (BIN \ DEBUG), и поэтому ваши предыдущие изменения теряются. Теперь ваше приложение снова вставляет / обновляет целевую таблицу и вы не можете найти никаких ошибок в коде. (Теперь я не знаю, являетесь ли вы таким парнем, но после двух или трех раз этого цикла я слышал разные уродливые слова)

Вы можете остановить эту проблему, изменив свойство Copy To Output Directory - Copy If Newer или Never Copy. Также вы можете обновить строку соединения в проводнике сервера, чтобы посмотреть на рабочую копию базы данных или создать второе соединение. Первый по-прежнему указывает на базу данных в папке проекта, а второй указывает на базу данных в папке BIN \ DEBUG. Таким образом, вы можете сохранить исходную базу данных в целях развертывания и изменения схемы, в то время как со вторым соединением вы можете посмотреть на эффективные результаты ваших усилий по кодированию.

EDIT Специальное предупреждение для базы данных MS-Access пользователи. Простой взгляд на таблицу изменяет дату изменения базы данных ТАКЖЕ, если вы ничего не пишете и не меняете. Таким образом, флаг Copy if Newer запускается, и файл базы данных копируется в выходной каталог. С Access лучше использовать Copy Never.

27
задан Jason Baker 31 May 2009 в 19:40
поделиться

2 ответа

Основным отличием между непосредственным вызовом (который является сокращением для Invoke(...)) и использованием DynamicInvoke является производительность; по моей оценке, в 700 с лишним раз (ниже).

При прямом / Invoke подходе аргументы уже предварительно проверены через сигнатуру метода, и уже существует код для прямой передачи этих аргументов в метод (я бы сказал «как IL», но я, кажется, напомню что среда выполнения обеспечивает это напрямую, без никакого IL). С помощью DynamicInvoke необходимо проверить их из массива с помощью отражения (то есть все ли они подходят для этого вызова; нужны ли они для распаковки и т. Д.); это медленно (если вы используете его в узком цикле), и его следует избегать, где это возможно.

Пример; Сначала результаты (я увеличил число LOOP из предыдущего редактирования, чтобы дать разумное сравнение):

Direct: 53ms
Invoke: 53ms
DynamicInvoke (re-use args): 37728ms
DynamicInvoke (per-cal args): 39911ms

С кодом:

static void DoesNothing(int a, string b, float? c) { }
static void Main() {
    Action<int, string, float?> method = DoesNothing;

    int a = 23;
    string b = "abc";
    float? c = null;
    const int LOOP = 5000000;

    Stopwatch watch = Stopwatch.StartNew();
    for (int i = 0; i < LOOP; i++) {
        method(a, b, c);
    }
    watch.Stop();
    Console.WriteLine("Direct: " + watch.ElapsedMilliseconds + "ms");

    watch = Stopwatch.StartNew();
    for (int i = 0; i < LOOP; i++) {
        method.Invoke(a, b, c);
    }
    watch.Stop();
    Console.WriteLine("Invoke: " + watch.ElapsedMilliseconds + "ms");

    object[] args = new object[] { a, b, c };
    watch = Stopwatch.StartNew();
    for (int i = 0; i < LOOP; i++) {
        method.DynamicInvoke(args);
    }
    watch.Stop();
    Console.WriteLine("DynamicInvoke (re-use args): "
         + watch.ElapsedMilliseconds + "ms");

    watch = Stopwatch.StartNew();
    for (int i = 0; i < LOOP; i++) {
        method.DynamicInvoke(a,b,c);
    }
    watch.Stop();
    Console.WriteLine("DynamicInvoke (per-cal args): "
         + watch.ElapsedMilliseconds + "ms");
}
33
ответ дан Marc Gravell 14 October 2019 в 13:48
поделиться

На самом деле функциональной разницы между ними нет. если вы откроете реализацию в отражателе, вы заметите, что DynamicInvoke просто вызывает DynamicInvokeImpl с тем же набором аргументов. Никакой дополнительной проверки не выполняется, и это не виртуальный метод, поэтому его поведение не может быть изменено производным классом. DynamicInvokeImpl - это виртуальный метод, в котором выполняется вся фактическая работа.

9
ответ дан 28 November 2019 в 05:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: