C# 在团结中,团结如何神奇地称一切为“一切”;接口;?

C# 在团结中,团结如何神奇地称一切为“一切”;接口;?,c#,unity3d,interface,C#,Unity3d,Interface,Unity有一个“接口”: IPInterDownHandler() 您只需实现 而Unity将在任何这样的行为中“神奇地”调用OnPointerDown 您不必注册它们、设置事件,也不必执行任何其他操作 从语法上讲,您只需将“IPInterDownHandler”和“public void on PointerDown”添加到类中,就可以神奇地获得这些消息 (如果你不是Unity开发人员-甚至在游戏运行时突然在编辑器中添加一个,它也会起作用!) 他们是怎么做到的,我又是怎么做到的? 所以,我想

Unity有一个“接口”:

IPInterDownHandler
()

您只需实现

而Unity将在任何这样的行为中“神奇地”调用
OnPointerDown

您不必注册它们、设置事件,也不必执行任何其他操作

从语法上讲,您只需将“IPInterDownHandler”和“public void on PointerDown”添加到类中,就可以神奇地获得这些消息

(如果你不是Unity开发人员-甚至在游戏运行时突然在编辑器中添加一个,它也会起作用!)

他们是怎么做到的,我又是怎么做到的? 所以,我想这样做:

public interface IGetNews
 {
 void SomeNews(string s);
 }
然后我可以向任何MonoBehavior添加
SomeNews

备选方案是显而易见的,我想知道Unity是如何实现这种“神奇”行为的。 (顺便说一句:我觉得他们不应该称这些“接口”,因为它根本不像接口——它有点相反!我想,你可以说它们神奇地从多个抽象类继承。)


旁白:

如果您以前没有使用过Unity,那么执行此操作的常规方法(因为我们无法访问Unity magic)就是向您的守护进程添加UnityEvent,它将发送有问题的消息:

public class BlahDaemon:MonoBehaviour
  {
  public UnityEvent onBlah;
  
    ...
    onBlah.Invoke();
假设您有Aaa、Bbb、Ccc类想要获取消息。只需连接Unity事件(通过在编辑器中拖动或在代码中拖动),例如:

public class Aaa:MonoBehaviour
  {
  void Awake()
    {
    BlahDaemon b = Object.FindObjectOfType<BlahDaemon>();
    b.onBlah.AddListener(OnBlah);
    }
  
  public void OnBlah()
    {
    Debug.Log("almost as good as Unity's");
    }
  }
公共等级Aaa:单一行为
{
无效唤醒()
{
BlahDaemon b=Object.FindObjectOfType();
b、 AddListener(onBlah);
}
公开无效
{
Log(“几乎和Unity的一样好”);
}
}

您基本上是在
Awake
中“注册”您的呼叫,您实际上是在使用神奇的Unity——不管它是什么。但是我想直接使用魔法。

您可以使用反射获取实现特定接口的程序集中的所有类型,然后实例化这些类型,并通过接口调用这些实例上的方法

var types = this.GetType().Assembly.GetTypes()
                                   .Where(t=>t.GetInterfaces().Contains(typeof(IGetNews)));
foreach (var type  in types)
{
    var instance = (IGetNews) Activator.CreateInstance(type);
    instance.SomeNews("news");
}

当涉及到XXXUpdate、OnCollisionXXX和其他单行为时,Unity注册的方式并不像人们普遍认为的那样是反射,而是一些内部编译过程

如何调用更新 不,Unity不使用System.Reflection,每次需要调用一个神奇的方法时,它都会找到一个。

相反,第一次访问给定类型的MonoBehavior时,通过脚本运行时检查底层脚本(或者 Mono或IL2CPP)是否定义了任何魔术方法,以及 信息被缓存。如果一个单一行为有一个特定的方法,它就是 添加到适当的列表中,例如,如果脚本具有更新方法 已定义它将添加到需要更新的脚本列表中 每一帧

在游戏过程中,Unity只是在这些列表中迭代并执行其中的方法——就这么简单。还有,这就是为什么如果 您的更新方法是public或private

在接口的情况下,我假设它做得更多,因为接口是必需的。否则,您只需像添加任何其他MonoBehavior方法一样添加该方法

我的假设(可能是错误的),它在这个游戏对象上使用了一个基本的GetComponents。然后迭代生成的数组并调用必须实现的方法,因为它来自接口

var types = this.GetType().Assembly.GetTypes()
                                   .Where(t=>t.GetInterfaces().Contains(typeof(IGetNews)));
foreach (var type  in types)
{
    var instance = (IGetNews) Activator.CreateInstance(type);
    instance.SomeNews("news");
}
您可以使用以下方法复制该模式:

NewsData data;
if(GetNews(out data))
{
    IGetNews [] getNews = data.gameObject.GetComponents<IGetNews>();
    foreach(IGetNews ign in getNews){ ign.SomeNews(); }
}
NewsData数据;
如果(获取新闻(输出数据))
{
IGetNews[]getNews=data.gameObject.GetComponents();
foreach(getNews中的IGetNews-ign){ign.SomeNews();}
}

GetNews是一个检查是否应该向对象发送某些新闻的方法。你可以把它想象成物理。光线投射,为光线投射命中赋值。如果该对象出于任何正当理由接收新闻,它将在此处填充一个数据引用。

Beauty,谢谢:不过。。当我提到“……如果在游戏中你突然添加了其中的一个,即使在场景已经运行时,实例化一个新的游戏对象,并添加一个组件,比如WHOA,它将从那个框架开始工作!”Unity不做每个引擎框架的反射吗?你认为这真的只是我们无法做到的令人讨厌的诡计吗?也就是说,它们遵循正在创建的组件或其他东西:或者我估计过高了,每一帧都做这样的反射,真的是微不足道的吗?thx:/link由@fafase的答案发布,显示Unity没有为此使用程序集扫描。您不需要为每个帧执行反射过程,而是检查唤醒,然后将找到的方法添加到manager对象上的容器中。我认为,在这种情况下,它会变得更麻烦,更不直接。此外,它还需要添加一个管理器,这实际上是Unity为所有单行为回调所做的。嗨,fafase,不,如果你想模拟Unity所做的事情,你必须在每一帧都做,就像Mark解释的那样,使用反射。有问题的组件可以在游戏中随时创建/销毁:/(除非我不知何故误解了你),那么你认为他们实际使用的是什么样的列表?简单数组?或列出通用等。啊。。这就是答案。谢谢你。小恶魔确实是在创建组件的层次上做到这一点的。混蛋!为什么我们不能访问它!我希望他们没有使用c中的
接口
语法。。。。。。。。因为它实际上与接口无关。(这是接口的“对立面”)同样,我的假设是场景对象具有每个方法的容器。最有可能是列表,因为在创建/销毁对象时需要添加和删除。此外,在第一次运行时调用Start,以后不再调用。因此,它需要存储到适当的时间。“建筑需要清醒。”乔埃布洛,我会瘦的