【问题标题】:null coalescing operator assignment to selfnull 合并运算符赋值给 self
【发布时间】:2016-05-31 11:25:45
【问题描述】:

我目前在 Unity 中设置了两个脚本来处理一些 UI 音频。一个是经理,另一个是为特定的 UI 元素播放声音。我所拥有的简化版本是这样的:

public class AudioUIManager : MonoBehaviour //Only one of these in the scene
{
    public AudioClip genericUISound; //This is set in the inspector.
}

public class AudioUITextAnimation : MonoBehaviour
{
    [SerializeField]
    private AudioClip specifiedUISound; //This is not set in the inspector
    [SerializeField]
    private AudioUIManager audioUIManager; // I get a reference to this elsewhere

    void Start()
    {
        //Use generic sounds from audio manager if nothing is specified.
        specifiedUISound = specifiedUISound ?? audioUIManager.genericUISound;
        print(specifiedUISound);
    }
}

我在这里想要实现的是让specifiedUISound 字段使用在检查器中分配给它的声音。如果没有分配声音,则使用 UI 管理器中的通用声音。这样可以节省我为数百万个需要相同声音的按钮分配相同的声音,但如果我愿意,我可以选择为一个按钮设置特定的声音。

然而,null-coalessing 运算符将null 分配给specifiedUISound,即使它为null 而audioUIManager 的声音不是。另外,如果我使用三元运算符来检查 null ,我可以让它工作,如下所示:

specifiedUISound = specifiedUIsound == null ? audioUIManager.genericUISound : specifiedUISound;

我是否误解了 null 合并运算符?为什么会这样?

编辑: Jerry Switalski 指出,这只发生在specifiedUISound 被序列化时。 (如果它是public 或者如果它具有[SerializeField] 属性)。谁能解释一下这里发生了什么?

【问题讨论】:

  • s = s ?? “测试”;在这里工作正常
  • 所以你是说foo = foo ?? bar 不起作用,但foo = foo == null ? bar : foo 起作用? Unity 没有改变 C# 规则,所以它一定是你的代码。您是否可能在创建minimal reproducible example 时走得太远,以至于您删除了太多相关代码? AudioClip是什么类型,是struct还是class?
  • 是的,我测试了这个例子,它重现了问题。如果您创建这两个类并将它们统一添加到两个不同的游戏对象中,那么它会重新创建它!如果我不以这种方式使用它,它可以正常工作。
  • 我相信audioUIManager.genericUISoundnull,而代码正在执行,并且稍后才会变为非空。您是否调试过这种情况并实际看到了这种行为?请在代码执行前输出audioUIManager.genericUISound的值。
  • @NineBerry 刚刚检查过,audioUIManager.genericUISound 事先不为空。此外,带有三元运算符的方法如果为 null 也将不起作用。

标签: c# serialization unity3d null null-coalescing-operator


【解决方案1】:

你遇到了 Unity 的custom equality operator

当 MonoBehaviour 有字段时,仅在编辑器中,我们不会将这些字段设置为“真正的空”,而是设置为“假空”对象。我们的自定义 == 运算符能够检查某物是否是这些假空对象之一,并做出相应的行为。虽然这是一个奇特的设置,但它允许我们将信息存储在假空对象中,当您在其上调用方法或向对象请求属性时,它会为您提供更多上下文信息。如果没有这个技巧,你只会得到一个 NullReferenceException,一个堆栈跟踪,但你不会知道哪个 GameObject 的 MonoBehaviour 的字段为空。

在编辑器中运行时,Unity 将序列化的null 替换为实际上不是null 的标记值。这允许他们在某些情况下提供更多信息的错误消息。

specifiedUISound 是否等于 null?那要看你怎么问了。 C# 有多个“平等”概念,包括data equality and reference equality

一些检查会说值相等:==Object.Equals

其他人会说他们不相等:??Object.ReferenceEquals

这种行为只会发生在编辑器中。在独立构建中运行时,任何null 值都将只是null

【讨论】:

    【解决方案2】:

    这不是您问题的完整答案和非常有趣的示例,但我进行了一些测试,看起来问题出在SerializeField 属性中。

    当我运行此程序时(someObj 由检查员分配,nullObj 留空):

        public AudioClip someObj;
    
        [SerializeField]
        private AudioClip nullObj;
    
        void Start () 
        {
            Debug.Log("nullObj == null : " + (nullObj == null));
            Debug.Log("someObj == null : " + (someObj == null));
    
            nullObj = nullObj ?? someObj;
    
            Debug.Log ("nullObj == null : " + (nullObj == null));
        }
    

    我有这张照片:

    但是去掉SerializeField 属性,可以让事情按预期工作:

        public AudioClip someObj;
    
        private AudioClip nullObj;
    
        void Start () 
        {
            Debug.Log("nullObj == null : " + (nullObj == null));
            Debug.Log("someObj == null : " + (someObj == null));
    
            nullObj = nullObj ?? someObj;
    
            Debug.Log ("nullObj == null : " + (nullObj == null));
        }
    

    给予:

    如此令人放心:

    我真的不知道问题的根源,但事实是 Unity3D 序列化字段破坏了 Mono 引擎中的空合并运算符。我仍然不知道如何,但可能只是由于更改了序列化类型的== 运算符??

    无论如何,我希望它至少有一点帮助。

    【讨论】:

    • 非常有趣,很好的发现!
    • 我希望它有所帮助,实际上您也可以通过将 nullObj 保留为 public 来重现它(Unity 将自动序列化此文件) - 然后即使为 null,给定的运算符也无法正常工作。跨度>
    • 太棒了,超级有帮助。我会等着看是否有人知道为什么会发生这种情况,如果没有,请接受!
    • @gjttt1 这个答案不正确。我还在调查中,会通知你的
    • 他对公开和连载的东西是正确的,但它确实只发生在编辑器中。构建它时它应该可以正常工作。
    猜你喜欢
    • 1970-01-01
    • 2011-11-16
    • 2010-10-13
    • 2021-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-01
    相关资源
    最近更新 更多