2012-02-22 3 views
5

Я хотел бы знать, есть ли какие-либо накладные расходы, вызванные использованием анонимных методов при создании рабочего фона.Есть ли накладные расходы при использовании анонимных методов?

, например:

public void SomeMethod() 
{ 
    BackgroundWorker worker = new BackgroundWorker(); 
    worker.DoWork += (sender, e) => 
    { 
     //large amount of code 
    } 

    worker.RunWorkerAsync(); 
} 

бы вышеприведенный пример быть лучше или хуже, чем определение //large amount of code в отдельный метод?

Возникли ли какие-либо накладные расходы при определении фонового рабочего метода в строке, особенно если часто вызывается SomeMethod()?

+0

Если есть большой объем кода, вы, вероятно, захотите реорганизовать его на небольшое количество кода, вызывающего другие методы ... – Chris

ответ

4

Существует небольшая разница в том, как обрабатываются именованные методы и анонимные методы, когда вы создаете из них делегат.

Делегирование анонимных методов кэшируется, поэтому есть небольшие накладные расходы для проверки того, существует ли делегат в кеше. С другой стороны, если вы запустите метод более одного раза, он будет повторно использовать кэшированный делегат вместо создания нового.

Делегаты для именованных методов не кэшируются, поэтому они будут создаваться каждый раз.

Кроме этого нет разницы. Анонимный метод будет создан во время компиляции и существует в коде, как обычный метод, только с именем, о котором знает только компилятор.

1

Всякий раз, когда анонимный метод (включая lambdas) закрывает переменные, компилятор создает класс для хранения этих переменных для вас. Всякий раз, когда создается делегат, новый экземпляр этого класса тоже. Это, очевидно, добавляет дополнительную работу для среды выполнения, но в большинстве случаев она вообще незначительна.

+0

Поэтому я предполагаю, что если нет закрытия, то он не будет создавать класс, чтобы он не будет иметь никакого значения? – Chris

+0

@Chris Да, компилятор достаточно умен, чтобы создать анонимный тип (удерживать анонимный метод плюс закрытые переменные) только тогда, когда это необходимо. –

+0

Класс закрытия не создается при создании делегата. Он создается до того, как вы используете закрытое локальное соединение, потому что доступ к локалям в «родительском» методе должен фактически использовать класс закрытия. – svick

0

Я тестировал это на днях (используя класс StopWatch). Насколько я мог сказать, что не было никакой заметной разницы в производительности между вызовом метода непосредственно ...

SomeMethod(); 

... или с помощью анонимного метода ...

() => SomeMethod(); 
+2

Компилятор оптимизирует необходимость создания анонимного класса для хранения анонимного метода, поскольку никакие переменные не закрываются. –

+0

Интересно, я думал, что у компилятора может быть рука. Два вопроса: если тело анонимного метода ссылается на переменные за пределами его области, это приведет к накладным расходам? И если да, то когда это накладные расходы будут встречены? Во время создания или выполнения анонимного метода? – Moonshield

0

Это главным образом влияет на читаемость - большое количество кода в одном месте почти никогда не хорошо ;-)

с точки зрения производительности см When is optimization premature?

2

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

Что касается ИЛ, сгенерированного, если лямбда не перекрывает какие-либо переменные, тогда сгенерированный код IL такой же, как если бы вы поместили код в обычный именованный метод (за исключением того, что сгенерированный метод имеет невыразимое имя) ,

С другой стороны, если вы закрываете какую-либо переменную, компилятор создает класс замыкания для хранения этой переменной в поле. И доступ к полю немного дороже, чем доступ к локальной переменной.

Подводя итог, если вы закрываете некоторые переменные, есть небольшие накладные расходы (в том числе больше объектов, которые нужно собрать мусором). В большинстве ситуаций это не имеет значения, и беспокоиться об этом было бы преждевременной оптимизацией. Но если вы считаете, что это имеет значение, вы должны прокомментировать код.

0

Это то, что декомпилятор сказал:

[CompilerGenerated] 
private static DoWorkEventHandler CS$<>9__CachedAnonymousMethodDelegate1; 

[CompilerGenerated] 
private static void <SomeMethod1>b__0(object sender, DoWorkEventArgs e) 
{ 
    throw new NotImplementedException(); 
} 

public void SomeMethod1() 
{ 
    BackgroundWorker worker = new BackgroundWorker(); 
    BackgroundWorker backgroundWorker = worker; 
    backgroundWorker.DoWork += (object sender, DoWorkEventArgs e) => throw new NotImplementedException(); 
    worker.RunWorkerAsync(); 
} 

public void SomeMethod2() 
{ 
    BackgroundWorker worker = new BackgroundWorker(); 
    worker.DoWork += worker_DoWork; 
    worker.RunWorkerAsync(); 
} 

private void worker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    throw new NotImplementedException(); 
} 

Edit:

Глядя на IL код есть только небольшие накладные расходы при создании/назначении метод делегирования в первый раз.

Смежные вопросы