不定义该运算符或预定运算符_自定义==运算符,我们应该保留它吗?

不定义该运算符或预定运算符

When you do this in Unity:

在Unity中执行此操作时:

1

if (myGameObject == null) {}

1

if ( myGameObject == null ) { }

Unity does something special with the == operator. Instead of what most people would expect, we have a special implementation of the == operator.

Unity对==运算符做了一些特殊的事情。 与大多数人期望的不同,我们使用了==运算符的特殊实现。

This serves two purposes:

这有两个目的:

1) When a MonoBehaviour has fields, in the editor only[1], we do not set those fields to “real null”, but to a “fake null” object. Our custom == operator is able to check if something is one of these fake null objects, and behaves accordingly. While this is an exotic setup, it allows us to store information in the fake null object that gives you more contextual information when you invoke a method on it, or when you ask the object for a property. Without this trick, you would only get a NullReferenceException, a stack trace, but you would have no idea which GameObject had the MonoBehaviour that had the field that was null. With this trick, we can highlight the GameObject in the inspector, and can also give you more direction: “looks like you are accessing a non initialised field in this MonoBehaviour over here, use the inspector to make the field point to something”.

1)当MonoBehaviour具有字段时,仅在编辑器中[1],我们不会将这些字段设置为“真实null”,而是设置为“ fake null”对象。 我们的自定义==运算符能够检查某些东西是否是这些虚假的空对象之一,并相应地执行操作。 尽管这是一个奇特的设置,但它允许我们将信息存储在伪null对象中,当您在该对象上调用方法或向该对象请求属性时,它会为您提供更多上下文信息。 没有这个技巧,您将只会得到NullReferenceException,即堆栈跟踪,但是您将不知道哪个GameObject的MonoBehaviour的字段为null。 使用此技巧,我们可以在检查器中突出显示GameObject,还可以为您提供更多指导:“看起来您在此处访问此MonoBehaviour中未初始化的字段,请使用检查器使该字段指向某个对象”。

purpose two is a little bit more complicated.

目的二比较复杂。

2) When you get a c# object of type “GameObject”[2], it contains almost nothing. this is because Unity is a C/C++ engine. All the actual information about this GameObject (its name, the list of components it has, its HideFlags, etc) lives in the c++ side. The only thing that the c# object has is a pointer to the native object. We call these c# objects “wrapper objects”. The lifetime of these c++ objects like GameObject and everything else that derives from UnityEngine.Object is explicitly managed. These objects get destroyed when you load a new scene. Or when you call Object.Destroy(myObject); on them. Lifetime of c# objects gets managed the c# way, with a garbage collector. This means that it’s possible to have a c# wrapper object that still exists, that wraps a c++ object that has already been destroyed. If you compare this object to null, our custom == operator will return “true” in this case, even though the actual c# variable is in reality not really null.

2)当您获得类型为“ GameObject” [2]的ac#对象时,它几乎不包含任何内容。 这是因为Unity是C / C ++引擎。 有关此GameObject的所有实际信息(其名称,它具有的组件列表,其HideFlags等)都位于c ++端。 c#对象唯一具有的是指向本机对象的指针。 我们称这些c#对象为“包装对象”。 这些c ++对象(例如GameObject)以及从UnityEngine.Object派生的其他所有对象的生命周期都得到明确管理。 当您加载新场景时,这些对象将被销毁。 或者当您调用Object.Destroy(myObject); 在他们。 c#对象的生存期通过垃圾收集器以c#方式进行管理。 这意味着可能有ac#包装器对象仍然存在,它包装了已经销毁的c ++对象。 如果将此对象与null进行比较,则在这种情况下,即使实际的c#变量实际上并非真的为null,我们的自定义==运算符也将返回“ true”。

While these two use cases are pretty reasonable, the custom null check also comes with a bunch of downsides.

尽管这两个用例非常合理,但是自定义null检查也带来了许多缺点。

    Going over all these upsides and downsides, if we were building our API from scratch, we would have chosen not to do a custom null check, but instead have a myObject.destroyed property you can use to check if the object is dead or not, and just live with the fact that we can no longer give better error messages in case you do invoke a function on a field that is null.

    回顾所有这些方面,如果我们是从头开始构建API,我们将选择不执行自定义的null检查,而是使用myObject.destroyed属性来检查对象是否已失效,如果您确实在null字段上调用了函数,那么我们将不再提供更好的错误消息。

    What we’re considering is wether or not we should change this. Which is a step in our never ending quest to find the right balance between “fix and cleanup old things” and “do not break old projects”. In this case we’re wondering what you think. For Unity5 we have been working on the ability for Unity to automatically update your scripts (more on this in a subsequent blogpost). Unfortunately, we would be unable to automatically upgrade your scripts for this case. (because we cannot distinguish between “this is an old script that actually wants the old behaviour”, and “this is a new script that actually wants the new behaviour”).

    我们正在考虑的是是否应该更改此设置。 这是我们永无止境地寻求在“修复和清理旧项目”与“不破坏旧项目”之间找到适当平衡的第一步。 在这种情况下,我们想知道您的想法。 对于Unity5,我们一直在致力于Unity自动更新脚本的能力(有关更多信息,请参见后续博客)。 不幸的是,在这种情况下,我们将无法自动升级您的脚本。 (因为我们无法区分“这是实际上需要旧行为的旧脚本”和“这是实际上需要新行为的新脚本”)。

    We’re leaning towards “remove the custom == operator”, but are hesitant, because it would change the meaning of all the null checks your projects currently do. And for cases where the object is not “really null” but a destroyed object, a nullcheck used to return true, and will if we change this it will return false. If you wanted to check if your variable was pointing to a destroyed object, you’d need to change the code to check “if (myObject.destroyed) {}” instead. We’re a bit nervous about that, as if you haven’t read this blogpost, and most likely if you have, it’s very easy to not realise this changed behaviour, especially since most people do not realise that this custom null check exists at all.[3]

    我们倾向于“删除自定义==运算符”,但很犹豫,因为它会更改您的项目当前执行的所有null检查的含义。 对于对象不是“真正为空”而是被销毁的对象的情况,使用nullcheck会返回true,如果我们对此进行更改,它将返回false。 如果要检查变量是否指向已损坏的对象,则需要更改代码以改为检查“是否(myObject.destroyed){}”。 对此,我们有点紧张,好像您还没有读过这篇博客文章一样,很可能您也没有读过,很容易就没有意识到这种改变的行为,尤其是因为大多数人没有意识到这个自定义null检查存在于全部。[3]

    If we change it, we should do it for Unity5 though, as the threshold for how much upgrade pain we’re willing to have users deal with is even lower for non major releases.

    如果我们更改它,我们应该为Unity5做,因为对于非主要版本,我们愿意让用户处理多少升级痛苦的阈值甚至更低。

    What would you prefer us to do? give you a cleaner experience, at the expense of you having to change null checks in your project, or keep things the way they are?

    您希望我们做什么? 给您带来更整洁的体验,而您却不得不在项目中更改空检查,或者保持现状不变?

    Bye, Lucas (@lucasmeijer)

    再见,卢卡斯( @lucasmeijer )

    [1] We do this in the editor only. This is why when you call GetComponent() to query for a component that doesn’t exist, that you see a C# memory allocation happening, because we are generating this custom warning string inside the newly allocated fake null object. This memory allocation does not happen in built games. This is a very good example why if you are profiling your game, you should always profile the actual standalone player or mobile player, and not profile the editor, since we do a lot of extra security / safety / usage checks in the editor to make your life easier, at the expense of some performance. When profiling for performance and memory allocations, never profile the editor, always profile the built game.

    [1]我们仅在编辑器中执行此操作。 这就是为什么当您调用GetComponent()查询不存在的组件时,您会看到C#内存分配正在发生的原因,因为我们正在新分配的伪null对象中生成此自定义警告字符串。 在内置游戏中不会发生这种内存分配。 这是一个很好的例子,为什么要对游戏进行性能分析,应该始终对实际的独立播放器或移动播放器进行分析,而不对编辑器进行分析,因为我们在编辑器中进行了许多额外的安全性/安全性/使用检查,让您的生活更轻松,但会牺牲一些性能。 对性能和内存分配进行性能分析时,切勿剖析编辑器,而始终剖析已构建的游戏。

    [2] This is true not only for GameObject, but everything that derives from UnityEngine.Object

    [2]这不仅适用于GameObject,而且适用于从UnityEngine.Object派生的所有内容

    [3] Fun story: I ran into this while optimising GetComponent<T>() performance, and while implementing some caching for the transform component I wasn’t seeing any performance benefits. Then @jonasechterhoff looked at the problem, and came to the same conclusion. The caching code looks like this:

    [3]有趣的故事:在优化GetComponent <T>()性能时遇到了这个问题,在为转换组件实现一些缓存时,我没有看到任何性能上的好处。 然后@jonasechterhoff查看了问题,并得出了相同的结论。 缓存代码如下所示:

    1

    2
    3
    4
    5
    6
    7
    8
    9
    10
    private Transform m_CachedTransform
    public Transform transform
    {
      get
      {
        if (m_CachedTransform == null)
          m_CachedTransform = InternalGetTransform();
        return m_CachedTransform;
      }
    }

    1

    2
    3
    4
    5
    6
    7
    8
    9
    10
    private Transform m_CachedTransform
    public Transform transform
    {
       get
       {
         if ( m_CachedTransform == null )
           m_CachedTransform = InternalGetTransform ( ) ;
         return m_CachedTransform ;
       }
    }

    Turns out two of our own engineers missed that the null check was more expensive than expected, and was the cause of not seeing any speed benefit from the caching. This led to the “well if even we missed it, how many of our users will miss it?”, which results in this blogpost :)

    事实证明,我们自己的两名工程师没有想到空检查比预期的要昂贵,并且导致未从缓存中获得任何速度收益。 这导致了“好吧,即使我们错过了它,我们有多少用户会错过它?”,这导致了本博文:)

    翻译自: https://blogs.unity3d.com/2014/05/16/custom-operator-should-we-keep-it/

    不定义该运算符或预定运算符

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值