Home > Back-end >  c# how access a non-static public variable from action
c# how access a non-static public variable from action

Time:06-13

I'm using a NuGet package, FFMpegCore, to transcode videos in a Windows Service created with VS 2022. The package calls an Action during the transcoding process that gives status information which I'd like to return to the user using their org and person IDs. The problem is that I don't know how to access a non-static variable from the action. The variables are defined as non-static because multiple users can be transcoding video at the same time:

public string org_int = "";
public string person-int = "";

They are instantiated when the class is created:

CADEMedia media = new CADEMedia("151", "9655");
...

public class CADEMedia {
    public CADEMedia(string _org_int, string person_int) {
        org_int = _org_int;
        person_int = _person_int;
    }
    ...

The call to the FFMpegCore transcoding function looks like:

await FFMpegArguments
.FromFileInput(inputFile)
.OutputToFile(outputFile, true, options => options
.WithCustomArgument(customArguments)
.WithFastStart())
.NotifyOnProgress(progressHandler, videoDuration)
.ProcessAsynchronously();

Then the action, progressHandler, looks like this but neither org_int or person_int are accessible here and that's the problem. I need these values to know where to send the progress:

public Action<double> progressHandler = new Action<double>(p =>
{
    signalR.SendSignalR(org_int, person_int, "message", p   "% Complete");
});

So, the primary question is: How can I access org_int and person_int from the Action? I can't change the Action because it's predefined in a 3rd party package that I didn't write.

CodePudding user response:

The problem is in the definition of progressHandler.

public Action<double> progressHandler = new Action<double>(p =>
{
    signalR.SendSignalR(org_int, person_int, "message", p   "% Complete");
});

Narrowing down to the basics:

public Action<double> progressHandler = new Action<double>(...);

Here, progressHandler is a field, and new Action<double>(...) is its field initializer. A field initializer is executed during instantiation of the containing class (CADEMedia). At that point in time, this is not available yet. As you already noticed, it is impossible to access org_int and person_int from within the initializer. That is because those properties simply do not exist yet. The compiler knows this, and will give an error:

CS0236
A field initializer cannot reference the non-static field, method, or property 'CADEMedia.org_int'

The solution is simple: replace = with =>.

public Action<double> progressHandler => new Action<double>(...);

This completely changes the code being generated. progressHandler is now a property, and new Action<double>(...) is the implementation of its getter. The getter is not executed until the property is actually used. At which point the containing class has already been properly instantiated and initialized.

Specifically:

  • A new action is created (as a result of new Action<double>(...) being evaluated) whenever .NotifyOnProgress(progressHandler, videoDuration) is called.
  • signalR.SendSignalR(org_int, person_int, "message", p "% Complete") is executed whenever FFMpegCore calls the progress handler; a (repeated) consequence of .ProcessAsynchronously() being called.

TL;DR

This should work fine:

public Action<double> progressHandler => new Action<double>(p =>
{
    signalR.SendSignalR(org_int, person_int, "message", p   "% Complete");
});
  • Related