使用 Unity3D 进行 Ninject

发布于 2024-10-22 07:30:40 字数 4810 浏览 1 评论 0原文

Unity3D 使用游戏对象。您可以向这些游戏对象添加组件,其中组件是继承基类的脚本(c# 或 js 语言)。 Unity 本身是用本机代码编写的。组件不能有构造函数,而是使用反射来查找是否有某些命名方法(OnStart、Update 等)。

我想我可以做以下事情,而不是因为缺少构造函数和其他真正烦人的事情而让我的眼睛流血:

public class SomeGameBehaviour
{
    public SomeGameBehaviour(IGameObject gameObject) { }
}

(Monobehaviour 是基类)

public class ComponentWrapper : MonoBehaviour, IGameObject { }

..然后我可以抓住 gameObject.Transform 或者你从 SomeGameBehaviour 中得到的东西,同时解耦它来自Unity强制的弱智。

问题: 我无法使用默认的注入行为,因为 Components/MonoBehaviours 没有也不可能有构造函数 - 如果您尝试,它会向您抛出错误,所以我推出了自己的 Provider。

public class UnityProvider : IProvider
{
    public object Create(IContext context)
    {
        var go = new GameObject(context.Request.Target.Name, typeof(ComponentWrapper));
        var c = go.GetComponent<ComponentWrapper>();

        return c;
    }

    public Type Type { get; private set; }
}

我可以在 Unity 编辑器中看到游戏对象已创建,并且 ComponentWrapper 已附加,但是 Ninject 向我抛出一个空引用错误,我无法弄清楚。它似乎对 IGameObject 或 Target 做了进一步的事情,从而扰乱了进程。

NullReferenceException: Object reference not set to an instance of an object
Ninject.Infrastructure.Language.ExtensionsForMemberInfo.GetParentDefinition (System.Reflection.MethodInfo method, BindingFlags flags)
Ninject.Infrastructure.Language.ExtensionsForMemberInfo.GetParentDefinition (System.Reflection.PropertyInfo property)
Ninject.Infrastructure.Language.ExtensionsForMemberInfo.IsDefined (System.Reflection.PropertyInfo element, System.Type attributeType, Boolean inherit)
Ninject.Infrastructure.Language.ExtensionsForMemberInfo.HasAttribute (System.Reflection.MemberInfo member, System.Type type)
Ninject.Selection.Heuristics.StandardInjectionHeuristic.ShouldInject (System.Reflection.MemberInfo member)
Ninject.Selection.Selector+<>c__DisplayClass3.<SelectPropertiesForInjection>b__2 (IInjectionHeuristic h)
System.Linq.Enumerable.Any[IInjectionHeuristic] (IEnumerable`1 source, System.Func`2 predicate)
Ninject.Selection.Selector.<SelectPropertiesForInjection>b__1 (System.Reflection.PropertyInfo p)
System.Linq.Enumerable+<CreateWhereIterator>c__Iterator1D`1[System.Reflection.PropertyInfo].MoveNext ()
System.Collections.Generic.List`1[System.Reflection.PropertyInfo].AddEnumerable (IEnumerable`1 enumerable)
System.Collections.Generic.List`1[System.Reflection.PropertyInfo].AddRange (IEnumerable`1 collection)
Ninject.Selection.Selector.SelectPropertiesForInjection (System.Type type)
Ninject.Planning.Strategies.PropertyReflectionStrategy.Execute (IPlan plan)
Ninject.Planning.Planner+<>c__DisplayClass2.<GetPlan>b__0 (IPlanningStrategy s)
Ninject.Infrastructure.Language.ExtensionsForIEnumerableOfT.Map[IPlanningStrategy] (IEnumerable`1 series, System.Action`1 action)
Ninject.Planning.Planner.GetPlan (System.Type type)
Ninject.Activation.Context.Resolve ()
Ninject.KernelBase.<Resolve>b__4 (IContext context)
System.Linq.Enumerable+<CreateSelectIterator>c__Iterator10`2[Ninject.Activation.IContext,System.Object].MoveNext ()
System.Linq.Enumerable.Single[Object] (IEnumerable`1 source, System.Func`2 predicate, Fallback fallback)
System.Linq.Enumerable.SingleOrDefault[Object] (IEnumerable`1 source)
Ninject.Planning.Targets.Target`1[T].GetValue (System.Type service, IContext parent)
Ninject.Planning.Targets.Target`1[T].ResolveWithin (IContext parent)
Ninject.Activation.Providers.StandardProvider.GetValue (IContext context, ITarget target)
Ninject.Activation.Providers.StandardProvider+<>c__DisplayClass2.<Create>b__1 (ITarget target)
System.Linq.Enumerable+<CreateSelectIterator>c__Iterator10`2[Ninject.Planning.Targets.ITarget,System.Object].MoveNext ()
System.Collections.Generic.List`1[System.Object].AddEnumerable (IEnumerable`1 enumerable)
System.Collections.Generic.List`1[System.Object]..ctor (IEnumerable`1 collection)
System.Linq.Enumerable.ToArray[Object] (IEnumerable`1 source)
Ninject.Activation.Providers.StandardProvider.Create (IContext context)
Ninject.Activation.Context.Resolve ()
Ninject.KernelBase.<Resolve>b__4 (IContext context)
System.Linq.Enumerable+<CreateSelectIterator>c__Iterator10`2[Ninject.Activation.IContext,System.Object].MoveNext ()
System.Linq.Enumerable+<CreateCastIterator>c__Iterator0`1[SomeGameBehaviour].MoveNext ()
System.Linq.Enumerable.Single[SomeGameBehaviour] (IEnumerable`1 source, System.Func`2 predicate, Fallback fallback)
System.Linq.Enumerable.Single[SomeGameBehaviour] (IEnumerable`1 source)
Ninject.ResolutionExtensions.Get[SomeGameBehaviour] (IResolutionRoot root, Ninject.Parameters.IParameter[] parameters)
ObjectFactory.GetInstance[SomeGameBehaviour] () (at Assets/Scripts/Core/ObjectFactory.cs:31)
Grid.Start () (at Assets/Scripts/World/Grid.cs:27)

Unity3D uses GameObjects. You add components to these gameobjects, where a component is a script (in c# or js) that inherits a base class. Unity itself is written in native code. Components can't have a constructor, and instead use reflection to find if you have certain named methods (OnStart, Update, etc.).

Instead of making my eyes bleed with lack of constructors and other really annoying things, I figured I could do the following:

public class SomeGameBehaviour
{
    public SomeGameBehaviour(IGameObject gameObject) { }
}

(Monobehaviour is the base class)

public class ComponentWrapper : MonoBehaviour, IGameObject { }

..then I could grab gameObject.Transform or what have you from SomeGameBehaviour, while decoupling it from the Unity enforced retardary.

The Problem:
I couldn't use the default injection behaviour because Components/MonoBehaviours don't and can't have constructors - it throws errors at you if you try, so I rolled my own Provider.

public class UnityProvider : IProvider
{
    public object Create(IContext context)
    {
        var go = new GameObject(context.Request.Target.Name, typeof(ComponentWrapper));
        var c = go.GetComponent<ComponentWrapper>();

        return c;
    }

    public Type Type { get; private set; }
}

I can see in the Unity editor that the gameobject gets created, and ComponentWrapper gets attached, however Ninject throws a null ref error at me which I can't figure out. It seems to be doing further stuff to either IGameObject or the Target that's upsetting the process.

NullReferenceException: Object reference not set to an instance of an object
Ninject.Infrastructure.Language.ExtensionsForMemberInfo.GetParentDefinition (System.Reflection.MethodInfo method, BindingFlags flags)
Ninject.Infrastructure.Language.ExtensionsForMemberInfo.GetParentDefinition (System.Reflection.PropertyInfo property)
Ninject.Infrastructure.Language.ExtensionsForMemberInfo.IsDefined (System.Reflection.PropertyInfo element, System.Type attributeType, Boolean inherit)
Ninject.Infrastructure.Language.ExtensionsForMemberInfo.HasAttribute (System.Reflection.MemberInfo member, System.Type type)
Ninject.Selection.Heuristics.StandardInjectionHeuristic.ShouldInject (System.Reflection.MemberInfo member)
Ninject.Selection.Selector+<>c__DisplayClass3.<SelectPropertiesForInjection>b__2 (IInjectionHeuristic h)
System.Linq.Enumerable.Any[IInjectionHeuristic] (IEnumerable`1 source, System.Func`2 predicate)
Ninject.Selection.Selector.<SelectPropertiesForInjection>b__1 (System.Reflection.PropertyInfo p)
System.Linq.Enumerable+<CreateWhereIterator>c__Iterator1D`1[System.Reflection.PropertyInfo].MoveNext ()
System.Collections.Generic.List`1[System.Reflection.PropertyInfo].AddEnumerable (IEnumerable`1 enumerable)
System.Collections.Generic.List`1[System.Reflection.PropertyInfo].AddRange (IEnumerable`1 collection)
Ninject.Selection.Selector.SelectPropertiesForInjection (System.Type type)
Ninject.Planning.Strategies.PropertyReflectionStrategy.Execute (IPlan plan)
Ninject.Planning.Planner+<>c__DisplayClass2.<GetPlan>b__0 (IPlanningStrategy s)
Ninject.Infrastructure.Language.ExtensionsForIEnumerableOfT.Map[IPlanningStrategy] (IEnumerable`1 series, System.Action`1 action)
Ninject.Planning.Planner.GetPlan (System.Type type)
Ninject.Activation.Context.Resolve ()
Ninject.KernelBase.<Resolve>b__4 (IContext context)
System.Linq.Enumerable+<CreateSelectIterator>c__Iterator10`2[Ninject.Activation.IContext,System.Object].MoveNext ()
System.Linq.Enumerable.Single[Object] (IEnumerable`1 source, System.Func`2 predicate, Fallback fallback)
System.Linq.Enumerable.SingleOrDefault[Object] (IEnumerable`1 source)
Ninject.Planning.Targets.Target`1[T].GetValue (System.Type service, IContext parent)
Ninject.Planning.Targets.Target`1[T].ResolveWithin (IContext parent)
Ninject.Activation.Providers.StandardProvider.GetValue (IContext context, ITarget target)
Ninject.Activation.Providers.StandardProvider+<>c__DisplayClass2.<Create>b__1 (ITarget target)
System.Linq.Enumerable+<CreateSelectIterator>c__Iterator10`2[Ninject.Planning.Targets.ITarget,System.Object].MoveNext ()
System.Collections.Generic.List`1[System.Object].AddEnumerable (IEnumerable`1 enumerable)
System.Collections.Generic.List`1[System.Object]..ctor (IEnumerable`1 collection)
System.Linq.Enumerable.ToArray[Object] (IEnumerable`1 source)
Ninject.Activation.Providers.StandardProvider.Create (IContext context)
Ninject.Activation.Context.Resolve ()
Ninject.KernelBase.<Resolve>b__4 (IContext context)
System.Linq.Enumerable+<CreateSelectIterator>c__Iterator10`2[Ninject.Activation.IContext,System.Object].MoveNext ()
System.Linq.Enumerable+<CreateCastIterator>c__Iterator0`1[SomeGameBehaviour].MoveNext ()
System.Linq.Enumerable.Single[SomeGameBehaviour] (IEnumerable`1 source, System.Func`2 predicate, Fallback fallback)
System.Linq.Enumerable.Single[SomeGameBehaviour] (IEnumerable`1 source)
Ninject.ResolutionExtensions.Get[SomeGameBehaviour] (IResolutionRoot root, Ninject.Parameters.IParameter[] parameters)
ObjectFactory.GetInstance[SomeGameBehaviour] () (at Assets/Scripts/Core/ObjectFactory.cs:31)
Grid.Start () (at Assets/Scripts/World/Grid.cs:27)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

回眸一笑 2024-10-29 07:30:40

Ninject 在创建对象后执行激活操作。在本例中,它是属性注入激活操作。看来反射部分存在问题,如果对象的属性上存在注入属性,则尝试获取信息。这可能是 Ninject 中的一个错误。但为了进一步调查,我需要一些信息:

  1. 您使用的是最新版本的 Ninject (2.2.1.0) 吗?
  2. 您具体使用哪个版本? (例如.NET 4.0 NoWeb)
  3. 您可以调试Ninject的StandardInjectionHeuristic.ShouldInject并调查哪个属性导致了问题吗?看看这个房产有什么特别之处? (例如,它是否是被覆盖的虚拟属性,是否有其他同名的属性,是否是索引器,...)

Ninject executes the activtion actions after the object is created. In this case it is the property injection activation action. It seems that there is a problem in the reflection part that tries to get the the information if there is an inject attribute on a property of your object. Probably that's a bug in Ninject. But for further investigation I need some information:

  1. Are you on the latest version of Ninject (2.2.1.0)?
  2. Which version do you use exactly? (e.g. .NET 4.0 NoWeb)
  3. Can you debug the StandardInjectionHeuristic.ShouldInject of Ninject and investigate which property is causing the problem? And see what's special about this property? (e.g. is it a virtual property that is overridden, are there other properties with the same name, is it an indexer, ...)
提笔落墨 2024-10-29 07:30:40

本文作者 http://outlinegames.com/2012/08/29/on -testability/ 成功将 Ninject 移植到 Unity,他将其命名为 Uniject:https://github.com/ banderous/Uniject

但是我也想指出我的(简单)解决方案:

http://blog.sebaslab.com/ioc-container-for-unity3d-part-1/
http://blog.sebaslab.com/ioc-container-for- unity3d-part-2/

我还是要感谢Remo Gloor,因为感谢他我更好地理解了IoC容器的概念。

The author of this article http://outlinegames.com/2012/08/29/on-testability/ managed to port Ninject to Unity and he called it Uniject: https://github.com/banderous/Uniject

However I want to point out my (simple) solution as well:

http://blog.sebaslab.com/ioc-container-for-unity3d-part-1/
http://blog.sebaslab.com/ioc-container-for-unity3d-part-2/

I still need to thank Remo Gloor, because thanks to him I understood better the concept of IoC container.

隔纱相望 2024-10-29 07:30:40

我想对此添加一些内容(尽管我的问题可能有所不同),因为我也想在 Unity3D 项目中使用 ninject。

不幸的是,Ninject 无法在使用 Mono 开发编译版本和源代码 (2.2.1) 的 Unity 3D 3.5 中工作。

然而使用源代码我明白了原因:首先我必须手动禁用与NOWEB定义相关的所有代码(不幸的是Unity3D不支持编译级别定义,如果我没记错的话),但真正的崩溃发生是因为NO_ASSEMBLY_SCANNING相关代码。正是在 Kernel.cs 中的这些行中:

#if !NO_ASSEMBLY_SCANNING
            if (this.Settings.LoadExtensions)
            {
                this.Load(new[] { this.Settings.ExtensionSearchPattern });
            }
#endif

我也尝试禁用此定义,然后 Unity3D 停止抱怨。虽然在我这样做之后,注入似乎停止工作:

StandardKernel kernel = new StandardKernel();   

kernel.Bind<IKernel>().ToMethod(context => kernel);

kernel.Inject(ObjectWhichNeedsKernel)

它不起作用,ObjectWhichNeedsKernel 没有注入 IKernel,但这可能只是我的代码错误。

所以我认为 Ninject 对于 Unity3D 来说还不够轻量:/

I want to add something about this (although my problem could be different), since I wanted to use ninject in an Unity3D project as well.

Unluckily Ninject does not work in Unity 3D 3.5 both using the mono develop compiled version and the source code (2.2.1).

However using the source code I understood the reason: First I had to disable manually all the code related to the NOWEB define (unluckily Unity3D does not support compile level defines, if I am not wrong) , but the real crash happens because of the NO_ASSEMBLY_SCANNING related code. Exactly in Kernel.cs at these lines:

#if !NO_ASSEMBLY_SCANNING
            if (this.Settings.LoadExtensions)
            {
                this.Load(new[] { this.Settings.ExtensionSearchPattern });
            }
#endif

I tried to disable this define as well and then Unity3D stopped to complain. Although after I did it the injection seemed to stop working:

StandardKernel kernel = new StandardKernel();   

kernel.Bind<IKernel>().ToMethod(context => kernel);

kernel.Inject(ObjectWhichNeedsKernel)

it did not work, the ObjectWhichNeedsKernel did not had the IKernel injected, but it could be just my code wrong.

So I think that Ninject is not lightweight enough for Unity3D :/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文