lambda 表达式中的只读引用变量

Posted

技术标签:

【中文标题】lambda 表达式中的只读引用变量【英文标题】:Read only reference variable in a lambda expression 【发布时间】:2022-01-17 05:04:08 【问题描述】:

这是我在这里的第一个问题。如果我在询问或格式化方面做错了什么,请告诉我!

我的程序必须在 Windows Media Player 控件中播放某些内容,等到播放完毕,然后继续播放另一个项目。

下面是整个函数:

    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;
        );
    

当然,Visual Studio 抱怨我可能不会在 lambda 表达式中使用引用变量。这是合乎逻辑的,因为在异步任务中修改引用变量会很糟糕,让我们保持不变。

但是,我不需要修改它,因为它在程序的其他地方被修改了。这就是为什么它是一个引用变量。

有没有办法以 Visual Studio 接受的方式读取此变量?也许让它成为一个只读变量?如果有,怎么做?

提前致谢, 利亚姆

【问题讨论】:

您需要确保在 lambda 运行时,您正在读取的任何内容在任何时候都仍然存在。 ref 可以引用局部变量,这就是它被禁止的原因。不要传递ref 参数,而是将其放在某个字段中并使用它。也许你的 lambda 甚至可以是一个包含该字段的类的方法。 【参考方案1】:

我找到了针对我的具体案例的答案。我将变量audioFileFinished 设为静态变量。任务中执行的方法现在位于与audioFileFinished 相同的类中。我的函数现在看起来像这样:

    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));
    

而主类中的方法是这样的:

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

        
        localPlayer.URL = path;
    

变量现在被初始化为public static bool audioFileFinished;

感谢@Etienne de Martel 的建议:

也许您的 lambda 甚至可以是包含该字段的类的方法。

【讨论】:

使用静态字段的一个缺点是您不能使用该类的多个实例。如果 audioFileFinished 设置为 true,则该类的所有实例都将看到该新值。这不是世界末日,但有一天有人可能会创建该类的第二个实例,然后想知道为什么它不能正常工作。【参考方案2】:

这是一种方法,虽然名称很糟糕,但您可能想更改:

public class Completion : ICompletionNotification

    public bool IsCompleted  get; private set; 

    public void Complete() => IsCompleted = true;


public interface ICompletionNotification

    bool IsCompleted  get; 

调用代码会创建一个新的Completion,但您的Play 方法采用ICompletionNotification 类型的参数

public void Play(AxWindowsMediaPlayer player, ICompletionNotification completion)

...并检查其IsCompleted 属性。

这样,调用者可以创建一个Completion 并将其传递给Play() 方法,该方法将其转换为ICompletionNotification

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

调用者可以调用Complete() 表示已经完成,但接收者不能设置该属性。它只能读取它。


CancellationTokenCancellationTokenSource 的工作方式几乎相同。正如文档所说,

CancellationToken 允许线程、线程池工作项或任务对象之间的协作取消。

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

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

这是同样的想法。创建者可以取消它,但它传递的令牌只能用于查看操作是否已取消。

我最初没有推荐它,因为从技术上讲,您并没有“取消”任何东西。但这是相同的概念,不需要定义新类型。看到它的人会明白你在做什么,或者可以查看现有文档。

【讨论】:

有趣的方法!事实上,该方法已经在一个具有接口的类中(播放方法是“接口的”),所以我可以将它添加到该接口中。但是,我找到了上面标记的另一个解决方案。如果我在开发这个应用程序的过程中遇到了关于我的解决方案的问题,也许我会试试你的!

以上是关于lambda 表达式中的只读引用变量的主要内容,如果未能解决你的问题,请参考以下文章

第13课 lambda表达式

15.lambda表达式

1.1 为什么要使用lambda 表达式

能用一个变量java8 的“方法引用”吗

lambda表达式传参

6-3 lambda 表达式