Home > Back-end >  Read only reference variable in a lambda expression
Read only reference variable in a lambda expression

Time:12-14

This is my first question here. If I do anything wrong regarding asking or formatting, please tell me!

My program must play something in a Windows Media Player control, wait until it is done playing, then continue with another item.

Below is the entire function:

    public void Play(AxWindowsMediaPlayer player, ref bool audioFileFinished)
    {
        int numberOfIntro = rnd.Next(songIntros.Count); //Randomly select an intro from the list
        string introFilePath = songIntros.ElementAt(numberOfIntro).fullPath;
        player.URL = introFilePath;

        //This task is necessary because the while (!audioFileFinished) will otherwise run in the UI and hang the app.
        Task f = Task.Factory.StartNew(() =>
        {
            while (!audioFileFinished)
            {
            }

            player.URL = fullPath;
        });
    }

Of course, Visual Studio complains that I may not use a reference variable in the lambda expression. This is logical, as modifying reference variables in asynchronous tasks would be bad, let's keep it at that.

But, I don't have the need to modify it, as it is modified somewhere else in the program. That's why it is a reference variable.

Is there a way to read this variable in a way that Visual Studio accepts? Maybe make it a read only variable? If so, how?

Thanks in advance, Liam

CodePudding user response:

I found an answer to my specific case. I made the variable audioFileFinished a static one. The method to execute in the task is now located in the same class as audioFileFinished. My function now looks like this:

    public void Play(AxWindowsMediaPlayer player)
    {
        int numberOfIntro = rnd.Next(songIntros.Count); //Randomly select an intro from the list
        string introFilePath = songIntros.ElementAt(numberOfIntro).fullPath;
        player.URL = introFilePath;

        //This task is necessary because the while (!audioFileFinished) will otherwise run in the UI and hang the app.
        Task f = Task.Factory.StartNew(() => Radio.PlayAfterThis(fullPath));
    }

And the method in the main class looks like this:

    public static void PlayAfterThis(string path)
    {
        while (!audioFileFinished)
        {

        }
        localPlayer.URL = path;
    }

The variable now gets initialised as public static bool audioFileFinished;

Thanks to @Etienne de Martel for suggesting this:

Maybe your lambda could even be a method of a class with that field in it.

CodePudding user response:

Here's one approach, with admittedly poor names that you might want to change:

public class Completion : ICompletionNotification
{
    public bool IsCompleted { get; private set; }

    public void Complete() => IsCompleted = true;
}

public interface ICompletionNotification
{
    bool IsCompleted { get; }
}

Calling code creates a new Completion, but your Play method takes an argument of type ICompletionNotification

public void Play(AxWindowsMediaPlayer player, ICompletionNotification completion)

...and checks its IsCompleted property.

That way the caller can create a Completion and pass it to the Play() method which casts it as ICompletionNotification.

var completion = new Completion();
Play(player, completion);

The caller can call Complete() to indicate that it's done, but the receiver can't set that property. It can only read it.


CancellationToken and CancellationTokenSource work pretty much the same way. As the docs say,

A CancellationToken enables cooperative cancellation between threads, thread pool work items, or Task objects.

using CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;

// pass the token to some method, and then when it's done call
source.Cancel();

It's the same idea. The creator can cancel it, but it passes the token which can only be used to see if the operation has been canceled.

I didn't initially recommend it because technically you're not "cancelling" anything. But it's the same concept and doesn't require defining a new type. Someone who sees it will understand what you're doing or can look up the existing documentation.

  • Related