Вызывать делегатов без параметров, но используя локальные параметры c #

2

Я нахожу, что делаю следующее много, и я не знаю, есть ли какие-либо побочные эффекты или нет, но рассмотрим следующее в приложении WinForms С#. (пожалуйста, извините любые ошибки, поскольку я набираю код, а не копирую вставку)

int a = 1;
int b = 2;
int c = 3;
this.Invoke((MethodInvoker)delegate()
{
    int lol = a + b + c;
});

С этим что-то не так? Или я должен делать длинный путь > _ <

int a = 1;
int b = 2;
int c = 3;
TrippleIntDelegate ffs = new TrippleIntDelegate(delegate(int a_, int b_, int c_)
{
   int lol = a_ + b_ + c_;
});

this.Invoke(ffs);

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

Как это важно? Могу ли я быть ленивым?

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

Теги:
delegates
lazy-evaluation
parameters

4 ответа

1
Лучший ответ

Нет ничего плохого в передаче локальных переменных, если вы понимаете, что получаете отсроченное исполнение. Если вы пишете это:

int a = 1;
int b = 2;
int c = 3;
Action action = () => Console.WriteLine(a + b + c);
c = 10;
action();  // Or Invoke(action), etc.

Результат этого будет 13, а не 6. Я полагаю, это было бы аналогией того, что сказал Томас; если вы читаете locals в делегате, он будет использовать любые значения, которые сохраняются переменными, когда действие действительно выполняется, а не когда оно объявлено. Это может привести к некоторым интересным результатам, если переменные содержат ссылочные типы, и вы вызываете делегат асинхронно.

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

  • 0
    Да, это именно то, что я хочу
3

Как вы его используете, на самом деле это не имеет никакого значения. Однако в первом случае ваш анонимный метод захватывает переменные, которые могут иметь довольно большие побочные эффекты, если вы не знаете, что делаете. Например:

// No capture :
int a = 1;
Action<int> action = delegate(int a)
{
    a = 42; // method parameter a
});
action(a);
Console.WriteLine(a); // 1

// Capture of local variable a :
int a = 1;
Action action = delegate()
{
    a = 42; // captured local variable a
};
action();
Console.WriteLine(a); // 42
  • 0
    Да, я знаю об этом: P
1

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

С другой стороны, ваш второй пример фактически передает данные через параметры. Это позволяет методу Invoke правильно маршалировать данные по границам потоков и избегать этих неприятных проблем с потоками. Если вы вызываете Invoke из, скажем, фонового работника, тогда вы должны использовать что-то вроде своего второго примера (хотя я бы предпочел использовать Action < T,... > и Func < T,... > делегаты, когда это возможно, а не создавать новые).

  • 0
    Фактически, передача локальных данных делегатам может устранить некоторые проблемы многопоточности, если все остается в области. Одним из таких примеров было бы распараллеливание задач с использованием ThreadPool , передача каждой из них ManualResetEvent для установки по завершении, а затем ожидание на всех дескрипторах ожидания. Гораздо проще, чем альтернативы (в любом случае .NET 3.5). Нет проблем с многопоточностью, подобной этой, хотя, очевидно, это становится проблемой, если вы раскручиваете поток и затем позволяете локальным пользователям выйти из области видимости до завершения потока (как я уже отмечал ранее).
  • 0
    Я думаю, что закрытие местных жителей - скользкий путь. Я могу думать об обратном сценарии, однако, когда речь идет о WinForms и элементах управления, элементы управления и их дочерние элементы и свойства могут быть доступны только потоку пользовательского интерфейса. Попытка получить доступ к любой их части из другого потока приводит к исключениям. Я считаю, что у WPF и других XAML-приложений есть похожие проблемы. Если вы исключите пользовательский интерфейс, то я не думаю, что возникнут какие-либо проблемы ... но когда у вас есть пользовательский интерфейс, его нужно знать.
0

С точки зрения стиля я бы выбрал вариант передачи paramater. Он выражает намерение намного проще передать args instad для принятия каких-либо амбиций (а также облегчает тестирование). Я имею в виду, вы могли бы сделать это:

public void Int32 Add()
{
    return this.Number1 + this.Number2
}

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

Я регулярно делаю это с помощью парм, подобных коллекциям, которые все равно используются с помощью ref, и не нужно явно "возвращать":

public List<string> AddNames(List<String> names)
{
    names.Add("kevin");
    return names;
}

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

  • 1
    Ну, я говорил больше о делегатах, а не о методах, это другая история
  • 0
    да. Я знаю. Моя точка зрения была для ясности по краткости. Верно как для делегатов, так и для методов.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню