如何自动解决C#依赖项?

15

我一直在了解Unity的依赖注入,我知道它是一种方法,可以将类与接口类型绑定。但我想知道的是,是否必须这样做?在下面的场景中,有一个TerrainGeneratorTileCreator在同一空间内。我该如何将TileCreator注入到生成器中作为依赖项?

enter image description here

http://geekswithblogs.net/danielggarcia/archive/2014/01/23/introduction-to-dependency-injection-with-unity.aspx介绍了注册类型的步骤,但我看到某个地方说只要类在Unity资源部分可见,就能实现自动注入,只是我不知道语法是什么(如果可能的话)。

更新

我可以把所有类放在一个文件里……但对于一个大系统来说,这可能相当繁琐。目前这是我要尝试的方法——比根本无法运行好。

更新

看起来Unity应该能够查看类的构造函数并自动执行这些解析,并将它们注入到我的类的构造函数中。这可行吗?


3
您确定 [tag:unity3d] 有依赖注入吗?您确定您没有阅读关于 Microsoft 的同名 [tag:unity] 吗?您提供的链接是在讨论 Microsoft 的 Unity,而不是 Unity Technologies 的 Unity。您想要达到什么样的目的并不十分清楚,请您能否进一步解释您的意图? - Scott Chamberlain
我确实在问关于Unity3d的问题,我没有意识到它们是两件不同的事情。谢谢你指出来。 - Ben
2
你绝不能修改MonoBehavior类的构造函数。你的问题过于专注于让依赖注入工作,而没有充分解释为什么需要首先让依赖注入工作,这被称为XY问题。重新思考你的问题,并增加更多关于你真正问题的细节(*"在同一空间中有TerrainGenerator和TileCreator"*),这样人们才能帮助你... - Scott Chamberlain
依赖注入在Unity3d中并非必需,有“更为适当”的处理方式可以在Unity的系统中实现,但如果没有更多关于你所尝试做的具体细节,我们无法告诉你应该采用哪种适当方式来处理它。 - Scott Chamberlain
@ScottChamberlain 这个类生成地形。随着时间的推移,它还会放置树木、不同类型的资源,并确定给定的地形是否为水域。我试图避免所有这些逻辑都在一个嵌套混乱的类中。 - Ben
4个回答

8

如果你正在寻找适用于Unity3d引擎的DI工具,也许这个工具可以满足你的需求(虽然我没有使用过,但它的反馈很积极)https://github.com/modesttree/Zenject

如果你要使用微软的Unity DI库,你应该可以这样做:

container.RegisterTypes(
    AllClasses.FromLoadedAssemblies(),
    WithMappings.FromMatchingInterface,
    WithName.Default);

太好了,这正是我在寻找的。感谢您突出显示如此干净的解决方案。我仍然希望它足够聪明,能自动查看类的构造函数并构建该类的依赖项。 - Ben

4
我通常使用以下代码。当我加载一个应用程序时,应用程序会在目录中查找所有Dlls。这样,当您使用反射加载类时,它会搜索Dlls和exes。您还可以添加更多的路径来搜索。
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(currentDomain_AssemblyResolve);

Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        string defaultFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

        string assemblyName = new AssemblyName(args.Name).Name;

        string assemblyNameDll = assemblyName + ".dll";
        string assemblyNameExe = assemblyName + ".exe";

        string assemblyPathDll = Path.Combine(defaultFolder, assemblyNameDll);
        string assemblyPathExe = Path.Combine(defaultFolder, assemblyNameExe);

        string assemblyPathToUse = null;
        if (File.Exists(assemblyPathDll))
        {
            assemblyPathToUse = assemblyPathExe;
        }
        else if (File.Exists(assemblyPathExe))
        {
            assemblyPathToUse = assemblyPathExe;
        }
        else
        {
            IEnumerable<string> merge = AssemblyFolders.Values;
            if (!string.IsNullOrEmpty(TempLoadingFolder))
            {
                merge = AssemblyFolders.Values.Union(new List<string>() { TempLoadingFolder });
            }
            foreach (var folder in merge)
            {
                assemblyPathDll = Path.Combine(folder, assemblyNameDll);
                assemblyPathExe = Path.Combine(folder, assemblyNameExe);

                if (File.Exists(assemblyPathDll))
                {
                    assemblyPathToUse = assemblyPathDll;
                    break;
                }
                else if (File.Exists(assemblyPathExe))
                {
                    assemblyPathToUse = assemblyPathExe;
                    break;
                }
            }
        }

        Assembly assembly = null;

        if (assemblyPathToUse != null && File.Exists(assemblyPathToUse))
        {
            assembly = Assembly.LoadFrom(assemblyPathToUse);
        }
        return assembly;
    }

1
不,您不必使用接口,也可以注册和解析具体类型。
例如,您可以按以下方式注册TerrainGeneratorTileCreator:
var myTileCreator = new TileCreator();
container.RegisterType<TerrainGenerator>(new PerThreadLifetimeManager(), new InjectionFactory(c => new TerrainGenerator(myTileCreator)));

解决 TerrainGenerator

TerrainGenerator generator = container.Resolve<TerrainGenerator>();

使用不同的 TileCreator 来解决 TerrainGenerator 问题:

TerrainGenerator generator = container.Resolve<TerrainGenerator>(new ParameterOverride("tileCreator", new TileCreator()));

您可能想阅读Unity依赖注入-模式和实践,以获取更多有用的信息,例如属性注入等。

希望这能帮到您。


1

不要认为将类放在同一个文件中或不放都无所谓。Unity需要知道如何根据类型创建实例。

如果使用RegisterInstance,每次调用Resolve时都会返回传递的特定对象。如果使用RegisterType(或对于具体类未注册),Unity将尝试通过使用具有最多参数的构造函数来实例化该类型。对于每个参数类型,Unity将尝试递归解析它们。

将接口类型映射到具体类型的注册是强制性的,但注册具体类型本身是可选的。

示例代码:

using Microsoft.Practices.Unity;
using System;

namespace Unity
{  
    interface IFooBar
    {
        string Message();
    }

    class Foo
    {
        string msg;

        public Foo()
        {
            msg = "Hello";
        }

        public override string ToString()
        {
            return msg;
        }
    }

    class Bar
    {
        private Foo _f;
        private IFooBar _fb;
        public Bar(Foo f, IFooBar fb)
        {
            this._f = f;
            this._fb = fb;
        }

        public override string ToString()
        {
            return _f.ToString() + " World " + _fb.Message();
        }
    }

    class FooBar : IFooBar
    {
        public string Message()
        {
            return "Unity!";
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            UnityContainer container = new UnityContainer();
            container.RegisterType<IFooBar, FooBar>(); // required
            container.RegisterType<Foo>(); // optional
            container.RegisterType<Bar>(); // optional

            var mybar = container.Resolve<Bar>();

            Console.WriteLine(mybar);
        }
    }
}

https://msdn.microsoft.com/en-us/library/microsoft.practices.unity.iunitycontainer_methods(v=pandp.20).aspx


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接