none
лямбда с несколькими делегатами в параметрах RRS feed

  • Вопрос

  • public class LambdaTest
    {
        void Main()
        {
    
            FindByFirstName(33, (p1, p2), (p3, p4) =>
            {
                
            });
        }
    
        void FindByFirstName(int s, Deleg1 d1)
        {
        }
    
        public int S;
        public delegate void Deleg1(int p1, double p2);
        public delegate void Deleg2(int p3, double p4);
    
    }
    

    Так сделать не дает, пишет Unexpected Token на запятой между делегатами в лямбде. Мне нужно внутри лямбы иметь доступ к переменным обоих делегатов, чтобы использовние было как-то так:
            FindByFirstName(33, (p1, p2), (p3, p4) =>
            {
                //1 2
            },
            {
                //3 4
            });
    

    Такое возможно? Делегаты это коллбеки, то есть мне надо добиться поведения, что при вызове FindByFirstName запускается ожидание срабатывания двух коллбеков, при этом для каждого обарботка в своем методе... С одним делегатом прекрасно работает, в второй втиснуть не получается...
    7 сентября 2011 г. 14:12

Ответы

  •  
    // v1
    Deleg1 d1 = (p1,p2) =>
    {
    };
    Deleg2 d2 = (p1,p2) =>
    {
    };
    FindByFirstName(1, d1, d2);
    
    
    // v2
    FindByFirstName(
    1, 
    (p1, p2) => 
    {
    }, 
    (p3, p4) => 
    {
    });
    
    • Помечено в качестве ответа Qwester33 7 сентября 2011 г. 14:55
    7 сентября 2011 г. 14:35
  • Зависит от того, что подразумевать под потокобезопасностью и под общей переменной.

    Если написать код вроде:

     

        class Program
        {
            static void FindByFirstName(int s, Deleg1 d1, Deleg2 d2)
            {
                Console.WriteLine("FindByFirstName 1: " + s);
                d1(0, 1);
                d2(0, 2);
                Console.WriteLine("FindByFirstName 2: " + s);
            }
    
            public int S;
            public delegate void Deleg1(int p1, double p2);
            public delegate void Deleg2(int p3, double p4);
    
            static void Main(string[] args)
            {
                int commonVar = 1;
    
                FindByFirstName(
                commonVar,
                (p1, p2) =>
                {
                    commonVar++;
                    Console.WriteLine("D1: " + commonVar);
                },
                (p3, p4) =>
                {
                    commonVar++;
                    Console.WriteLine("D2: " + commonVar);
                });
    
                Console.WriteLine("Main: " + commonVar);
            }
        }
    
    

     


    то сработает механизм замыканий. Компилятор создат класс с именем вроде <>c__DisplayClass2, с полем commonVar и двумя методами (с телом как у лямбд). Добавит в метод Main локальную переменную этого класса, вроде <>c__DisplayClass2 CS$<>8__locals3.

    обращение в commonVar в лямбдах превратиться в обращение к this.commonVar. Обращение к commonVar в методе Main превратится в обращению к полю локальной переменной, что-то вроде CS$<>8__locals3.commonVar. FindByFirstName получит просто копию значения CS$<>8__locals3.commonVar на момент своего вызова.

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

    Не уверен, насколько получилось объяснить, почитайте, например, http://www.rsdn.ru/article/csharp/Closure_in_Csharp.xml, если хотите понять как работают замыкания.

    • Изменено PashaPashModerator 7 сентября 2011 г. 15:59
    • Помечено в качестве ответа Qwester33 8 сентября 2011 г. 10:10
    7 сентября 2011 г. 15:56
    Модератор
  • > будет ли потокобезопасным использование общей переменной

    если значение не меняется в процессе работы потоков, то да. если меняется, то см.
    http://msdn.microsoft.com/en-us/library/system.threading.interlocked.aspx

    для асинхронной работы лучше использовать TPL. например: 

    void FindByFirstName(int s, Deleg1 d1, Deleg2 d2)
    {
        var t1 = Task.Factory.StartNew(ts => // выполняется в отдельном потоке
        {
            d1((int)ts, 0.0);
        }, s);
        var t2 = Task.Factory.StartNew(ts => // выполняется в отдельном потоке
        {
            d2((int)ts, 0.0);
        }, s);
        // дождаться завершение потоков/задач
        Task.WaitAll(t1, t2);
    }
    

    • Помечено в качестве ответа Qwester33 8 сентября 2011 г. 10:10
    7 сентября 2011 г. 16:07

Все ответы

  •  
    // v1
    Deleg1 d1 = (p1,p2) =>
    {
    };
    Deleg2 d2 = (p1,p2) =>
    {
    };
    FindByFirstName(1, d1, d2);
    
    
    // v2
    FindByFirstName(
    1, 
    (p1, p2) => 
    {
    }, 
    (p3, p4) => 
    {
    });
    
    • Помечено в качестве ответа Qwester33 7 сентября 2011 г. 14:55
    7 сентября 2011 г. 14:35
  • Спасибо, второй вариант супер, так как сохраняется возможность использования общей переменной.

    Единственно, хочу уточнить по потокам. Если делегаты это ссылки на методы-обработчики функции обратного вызова, то есть выполняться будут не последовательно, а в произвольные моменты, то будет ли потокобезопасным использование общей переменной (которая в примере обозначена цифрой "1"), в обоих лямбдах? При одной лямбде работало нормально, при этом содержимое выражения выполнялось не в том потоке, в котором вызывалось FindByFirstName, а теперь ведь содержимое обоих выражений возможно тоже будет выполнено в разных потоках (в дополнение к тому потоку, который вызвал FindByFirstName)?

    7 сентября 2011 г. 15:04
  • Зависит от того, что подразумевать под потокобезопасностью и под общей переменной.

    Если написать код вроде:

     

        class Program
        {
            static void FindByFirstName(int s, Deleg1 d1, Deleg2 d2)
            {
                Console.WriteLine("FindByFirstName 1: " + s);
                d1(0, 1);
                d2(0, 2);
                Console.WriteLine("FindByFirstName 2: " + s);
            }
    
            public int S;
            public delegate void Deleg1(int p1, double p2);
            public delegate void Deleg2(int p3, double p4);
    
            static void Main(string[] args)
            {
                int commonVar = 1;
    
                FindByFirstName(
                commonVar,
                (p1, p2) =>
                {
                    commonVar++;
                    Console.WriteLine("D1: " + commonVar);
                },
                (p3, p4) =>
                {
                    commonVar++;
                    Console.WriteLine("D2: " + commonVar);
                });
    
                Console.WriteLine("Main: " + commonVar);
            }
        }
    
    

     


    то сработает механизм замыканий. Компилятор создат класс с именем вроде <>c__DisplayClass2, с полем commonVar и двумя методами (с телом как у лямбд). Добавит в метод Main локальную переменную этого класса, вроде <>c__DisplayClass2 CS$<>8__locals3.

    обращение в commonVar в лямбдах превратиться в обращение к this.commonVar. Обращение к commonVar в методе Main превратится в обращению к полю локальной переменной, что-то вроде CS$<>8__locals3.commonVar. FindByFirstName получит просто копию значения CS$<>8__locals3.commonVar на момент своего вызова.

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

    Не уверен, насколько получилось объяснить, почитайте, например, http://www.rsdn.ru/article/csharp/Closure_in_Csharp.xml, если хотите понять как работают замыкания.

    • Изменено PashaPashModerator 7 сентября 2011 г. 15:59
    • Помечено в качестве ответа Qwester33 8 сентября 2011 г. 10:10
    7 сентября 2011 г. 15:56
    Модератор
  • > будет ли потокобезопасным использование общей переменной

    если значение не меняется в процессе работы потоков, то да. если меняется, то см.
    http://msdn.microsoft.com/en-us/library/system.threading.interlocked.aspx

    для асинхронной работы лучше использовать TPL. например: 

    void FindByFirstName(int s, Deleg1 d1, Deleg2 d2)
    {
        var t1 = Task.Factory.StartNew(ts => // выполняется в отдельном потоке
        {
            d1((int)ts, 0.0);
        }, s);
        var t2 = Task.Factory.StartNew(ts => // выполняется в отдельном потоке
        {
            d2((int)ts, 0.0);
        }, s);
        // дождаться завершение потоков/задач
        Task.WaitAll(t1, t2);
    }
    

    • Помечено в качестве ответа Qwester33 8 сентября 2011 г. 10:10
    7 сентября 2011 г. 16:07