我一直在阅读有关Unity的依赖注入的内容,我理解这是一件事,并且它允许您在界面中键入类。我很好奇的是,我必须这样做吗?在下面的场景中,同一空间中有TerrainGenerator
和TileCreator
。如何将生成器中的TileCreator
作为依赖项?
http://geekswithblogs.net/danielggarcia/archive/2014/01/23/introduction-to-dependency-injection-with-unity.aspx引导我注册一个类型,但我读到某个地方,只要该类在Unity Assets部分中可见,它就能自动注入它,我只是无法弄清楚语法(如果可能的话)。
更新
我可以将所有类放在一个文件中......使用一个非常烦人的大型系统。与此同时,这是一种我会尝试的方法 - 比让它根本不起作用更好。
更新 看起来像Unity应该能够查看一个类的构造函数并自动执行这些解决方案并将它们注入我的类'构造函数中。这可能吗?
答案 0 :(得分:8)
如果您正在为Unity3d引擎寻找DI,也许这样可行(我没有使用它,但反馈是积极的)https://github.com/modesttree/Zenject
如果您正在谈论Microsoft的Unity DI库,您应该可以这样做:
container.RegisterTypes(
AllClasses.FromLoadedAssemblies(),
WithMappings.FromMatchingInterface,
WithName.Default);
答案 1 :(得分:4)
我总是使用以下代码。当我加载应用程序时,应用程序在目录中查找所有Dll。这样,当您使用反射加载类时,它会搜索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;
}
答案 2 :(得分:1)
不,您不必使用接口,您也可以注册和解决具体类型。
例如,您可以按如下方式注册 TerrainGenerator 和 TileCreator :
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()));
您可能需要阅读Dependency Injection with Unity - Patterns and Practices以获取更多有用信息,例如属性注入等。
希望有所帮助。
答案 3 :(得分: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);
}
}
}