Home > Software engineering >  How to create a timer callback that can edit its state object?
How to create a timer callback that can edit its state object?

Time:12-28

I have a System.Threading.Timer that receives some data each second, and I want its callback to put the received data into an object. I tried to implement that using C#'s reference types mechanism. Here's the current callback:

private void Receive(object? obj) {
  var data = GetData();

  obj = (object)data; // Does nothing
}

And here's the method that starts the timer (timer is a field):

public void Start(Image image) {
  timer = new Timer(
    Receive,
    image,
    TimeSpan.FromSeconds(1),
    TimeSpan.FromSeconds(1)
  );
}

Expected behavior: each second the data (which is image data) is received and put to the image, the reference to which is specified in parameter image.

Actual behavior: when I defined a variable of type Image, passed it to the Start method and checked its value (using the Visual Studio debugger) after 5 seconds, it was the empty Bitmap I assigned to it at the very start.

I have also checked that the data (containing non-empty image) is sent properly.

Can anybody tell me what's the issue, or, alternatively, suggest a different way to change the "state" object in a timer callback?

CodePudding user response:

obj = (object)data; only sets the data locally in the Receive method. You could fix it by creating a wrapper object

public class ImageData
{
    public Image Image { get; set; }
}

Then create a static instance of it that can be accessed from where you need it

public static readonly ImageData Data = new ImageData();

Depending on where you must access the images it can also an instance member and be private; however, it must always be the same instance that you used to start the timer.

Then start like this

public void Start() {
  timer = new Timer(
    Receive,
    Data,
    TimeSpan.FromSeconds(1),
    TimeSpan.FromSeconds(1)
  );
}

And receive

private void Receive(object? obj) {
  var data = (ImageData)obj;  
  data.Image = GetData();
}

Now, you can access the new images through the static Data.Image property.

CodePudding user response:

In the Recieve method you get an obj as input reference. Then you try to set a value to obj. This last step cannot work, since you work on the reference. The only way it could work is, if the input reference is given as a ref, which is

private void Receive(ref object? obj) {}

Passing a reference type by reference enables the called method to replace the object to which the reference parameter refers in the caller.

  • Related