【问题标题】:c# method override in Unity3dc# Unity3d中的方法覆盖
【发布时间】:2016-10-29 03:41:22
【问题描述】:

在最近几周,我使用 c# 作为脚本语言来玩 Unity3d。 我在 c# 和 Unity3d 之前从未使用过,我试图了解方法覆盖在 Unity 框架中是如何工作的。 令我惊讶的是,我可以扩展基类MonoBehavior 并覆盖Start ()Update () 等方法,而不必使用override 关键字,编译器也不会抱怨! 相反,在“普通”C# 程序中,我必须使用 override 关键字,否则会收到编译器警告。例如:

public class BaseClass {
    public virtual void MyMethod () {
        Console.WriteLine("BaseClass.MyMethod()");
    }
}

public class SubClass : BaseClass {
    public override void MyMethod () {
        Console.WriteLine("SubClass.MyMethod()");
    }
}

class Program {
    static void Main ()  {
        SubClass obj = new SubClass ();
        obj.MyMethod ();
    }
}

在 Unity 中我可以简单地做:

class PlayerController : MonoBehaviour {
    void Update () {
        // code...
    }
}

幕后发生了什么? unity是使用“动态技巧”还是什么?

【问题讨论】:

标签: c# unity3d


【解决方案1】:

因为MonoBehavior 没有Update 的定义。运行时检查是否存在名为 Update 的方法(使用反射),如果存在则调用它,但您没有覆盖基本实现

更新

由于您要求提供有关如何实现的示例,因此我在这里制作了一个示例:https://dotnetfiddle.net/UrpYkf

【讨论】:

  • 我想知道他们为什么选择这种方法而不是可以覆盖的空方法。
  • 有趣!您能否提供一个伪代码示例来说明它是如何工作的?
  • @Aphelion 因为这样引擎将不得不调用该方法,即使对于不覆盖它的类也是如此(好吧,他们可以在初始化时检查它是否不是默认方法,但性能与当前方法相同)。也许他们觉得接口对初学者来说太高级了(更多的机会搞砸了,比如定义了方法但没有实现接口)。
  • @Aphelion 我真的没测过,但我觉得反思慢的部分是在找方法。调用一个应该(理论上)比虚拟调用慢一点。不确定 Unity 是否有任何类型的特定 clr 或 jit 优化,但如果有,它实际上可以只使用 call 而不是 callvirt (甚至内联这些调用),一旦方法查找它实际上会更快已经完成了。同样,我实际上不知道它是否会这样做,但它肯定可以
  • @Aphelion 我记得在某处的博客文章中读到,“反射”是在编译时静态完成的,而在运行时,只有一个基于相关类型的简单查找表。调用反射方法确实更慢,但在这种情况下,反射是静态的,只有映射是动态的,跳过了反射的缺点。
【解决方案2】:

来源bunny83http://answers.unity3d.com/answers/177292/view.html

Unity 的工作方式有点不同。所有那些所谓的“事件”都不是 MonoBehaviour 类的方法。如果你有一个 C# 反射器,你可以 只需打开 UnityEngine.dll 即可查看类实现

Unity 主要是用 C++ 编写的,因此大多数魔法都发生在 本机代码。 Unity 使用反射来确定,在您的脚本之后 已经编译,你实现的那些“事件”是什么? 记住这堂课。 Unity 只调用 Update / LateUpdate / 实现后的 OnGUI。

您可以注意到的另一点是,当您创建一个 MonoBehaviour 类并且不实现任何这些方法/事件 Unity 将从您的脚本实例中删除已启用的复选框 因为它不包含任何受启用影响的事件。

有些事件是通过它们的 SendMessage 函数调度的(比如 OnCollisionEnter 等事件)。

文档将所有事件称为“可覆盖函数”,但那是 不是真的。如果它们被实现为虚拟功能,你 必须使用 override 关键字,否则将不起作用。

这是 MonoBehaviour 的完整实现:​​

public class MonoBehaviour : Behaviour
 {
     public extern bool useGUILayout
     {
         get;
         set;
     }
     public extern MonoBehaviour();
     private extern void Internal_CancelInvokeAll();
     private extern bool Internal_IsInvokingAll();
     public extern void Invoke(string methodName, float time);
     public extern void InvokeRepeating(string methodName, float time, float repeatRate);
     public void CancelInvoke();
     public extern void CancelInvoke(string methodName);
     public extern bool IsInvoking(string methodName);
     public bool IsInvoking();
     public Coroutine StartCoroutine(IEnumerator routine);
     public extern Coroutine StartCoroutine_Auto(IEnumerator routine);
     public extern Coroutine StartCoroutine(string methodName, object value);
     public Coroutine StartCoroutine(string methodName);
     public extern void StopCoroutine(string methodName);
     public extern void StopAllCoroutines();
     public static void print(object message);
 }

我删除了所有的外部属性,因为几乎每个 方法/属性映射到本机函数。

顺便说一句。提到的 C# 事件是委托,它们只是某种 函数指针,但有点复杂;)

【讨论】:

    【解决方案3】:

    MonoBehaviour 基类中没有声明 Start、Update、Awake 等。这就是为什么在声明它们时不编写任何 Override 关键字的原因。如果它们在您的类中实现,它们将被 Unity3D 调用,这可以提高性能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-15
      • 2010-11-16
      • 1970-01-01
      • 1970-01-01
      • 2012-02-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多