2016-06-09 9 views
2

У меня есть класс, который вызывает событие. Я хочу, чтобы подписчик мог изменять значения, передаваемые в EventArg.Не похоже, чтобы передать EventArgs по ссылке

В классе, который поднимает события:

class Factory 
    { 
    public event EventHandler<MessageReceivedEventArgs> MessageReceived; 

    private IServerLib _myObject; 

    public void Connect() 
    { 
     _myObject = new ServerLib(); 
     _myObject.AddMessageReceivedHandler((short terminal, ref string message, ref short functionNo) => 
     { 
      MessageReceivedEventArgs args = new MessageReceivedEventArgs { Terminal = terminal, Message = message, FunctionNo = functionNo }; 
      MessageReceivedEvent(ref args); 
     }); 
    } 

    private void MessageReceivedEvent(ref MessageReceivedEventArgs args) 
    { 
     EventHandler<MessageReceivedEventArgs> handler = MessageReceived; 
     if (handler != null) 
     { 
      handler(this, args); 
     } 
    } 

    public class MessageReceivedEventArgs : EventArgs 
    { 
     public short Terminal { get; set; } 
     public string Message { get; set; } 
     public short FunctionNo { get; set; } 
    } 
} 


interface IServerLib 
    { 
     void AddMessageReceivedHandler(MessageReceivedEventHandler action); 
    } 
    public delegate void MessageReceivedEventHandler(short terminal, ref string message, ref short functionNo); 

Абонент (который бывает VB) выглядит следующим образом:

Dim WithEvents _va As MyAssembly.MyClass 

Private Sub _va_MessageReceived(sender As Object, e As Factory.MessageReceivedEventArgs) Handles _va.MessageReceived 
    Debug.WriteLine($"Message: {e.Message} Terminal: {e.Terminal} Function: {e.FunctionNo}") 
    If e.Message = "1" Then 
     e.Message = "" 
     e.FunctionNo = 0 
     Debug.WriteLine("Cancelled") 
    End If 
End Sub 

Это вызывает событие, но установка e.Message и e.Function, похоже, не устанавливают значения. Я делаю что-то неправильно?

+0

Если вы считаете, что ваши параметры «ref» будут затронуты, тогда да - вы делаете что-то неправильно, они не будут. Вместо этого передайте экземпляр MessageReceivedEventArgs в MessageReceivedEvent. – Evk

+0

@Evk - Я считаю, что я делаю это 'handler (это, args);' 'args' является экземпляром –

+0

Вы можете показать код, который действительно компилируется? (Так как '(short x, ref string y, ref short z) =>' не разрешено, этот код кажется ошибочным. –

ответ

2

Проблема заключается в использовании этой линии:

var args = new MessageReceivedEventArgs 
      { Terminal = terminal, Message = message, FunctionNo = functionNo }; 

Он копирует все переменные в класс событий args. Изменение его там автоматически не меняет его на другом конце (где вы добавили ref). Не хорошее решение, но доказательство для вас это проблема, добавьте это после handler(this, args):

message = args.Message; 
functionNo = args.FunctionNo; 

Это заставит ref s перезаписать значение.

+0

Ах да - я вижу сейчас. Знаете ли вы о любом «хорошем» решении. Например, в рамке в «CancelEventArgs» FormClosing выполняется, например, знаете ли вы, как это можно сделать? –

+0

Да, и там они используют значение внутри метода приема. Результат не проходит за пределами границ метода (и если да, то они, вероятно, используют возвращаемое значение) –

+0

Это действительно достойное решение. Вы можете передать один объект, а не передавать значения по ссылке. –

1

Кажется, вы считаете, что ваш параметр «ref» должен быть затронут указанным выше кодом, но это неверно. Да, вы передаете свой message ссылкой на функцию MessageReceivedEvent, но тогда вы назначаете его MessageReceivedEventArgs.Message, и это происходит по значению, а не по ссылке.

В результате при изменении MessageReceivedEventArgs.Message в коде VB - message переменная не влияет (но MessageReceivedEventArgs.Messageэто влияет конечно), несмотря на то что вы прошли его по ссылке, как это должно быть.

Что вы должны сделать вместо этого проходит экземпляр MessageReceivedEventArgs вашей функции напрямую (не создавая его внутри этой функции):

private void MessageReceivedEvent(MessageReceivedEventArgs args) 
{ 
    EventHandler<MessageReceivedEventArgs> handler = MessageReceived; 
    if (handler != null) 
    { 
     handler(this, args); 
    } 
} 
+0

Это все еще не работает. Теперь я изменил аргумент, как вы предложили, и создал экземпляр MessageReceivedEventArgs в методе Connect и передал его , –

+0

Он должен работать, если он не обновляет ваш вопрос новым кодом (и уточняет ваши цели). – Evk

+1

Передача 'args' не помогает, так как он по-прежнему не копирует' ref '. –

0

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

class Factory 
{ 
    public event EventHandler<MessagereceivedEventArgs> MessageReceived; 

    void ReceiveMessage(string Message) 
    { 
     // Do something with the Message 

     // Then let your subscribers know that the message has been processed: 
     if (MessageReceived != null) 
     { 
      var ea = new MessageReceivedEventArgs(); 
      ea.Message = Message; 
      // Set ea properties as appropriate 
      MessageReceived(this, ea); 
      // Check ea properties for change 
      if (ea.Message != Message) 
      { 
       // A subscriber has changed the message in the MessageReceivedEventArgs 
      } 
     } 
    } 
}