Reputation: 62454
I've been reading about Unity's dependency injection and I understand it's a thing and that it allows you to type a class to an interface. What I'm curious about is, do I HAVE to? In the below scenario there's a TerrainGenerator
and TileCreator
in the same space. How can I get the TileCreator
within the generator as a dependency?
http://geekswithblogs.net/danielggarcia/archive/2014/01/23/introduction-to-dependency-injection-with-unity.aspx walks me through registering a type, but I read somewhere that as long as the class is visible in the Unity Assets section it'll be able to auto inject it, I just can't figure out the syntax (if it's possible).
Update
I could put all the classes in a single file... with a large system that could be pretty annoying. In the meantime it's an approach I'll try - better than having it not work at all.
update Seems like Unity should be able to look at a class' constructor and perform these resolutions automatically and inject them in my class' constructor. Is that possible?
Upvotes: 15
Views: 3124
Reputation: 3096
No, you don't have to use interfaces, you can register and resolve concrete types as well.
For example, you can register the TerrainGenerator and TileCreator as follows:
var myTileCreator = new TileCreator();
container.RegisterType<TerrainGenerator>(new PerThreadLifetimeManager(), new InjectionFactory(c => new TerrainGenerator(myTileCreator)));
To resolve TerrainGenerator:
TerrainGenerator generator = container.Resolve<TerrainGenerator>();
To resolve TerrainGenerator with a different TileCreator:
TerrainGenerator generator = container.Resolve<TerrainGenerator>(new ParameterOverride("tileCreator", new TileCreator()));
You may want to read Dependency Injection with Unity - Patterns and Practices for more useful information like properties injection and alike.
Hope that helps.
Upvotes: 1
Reputation: 531
Don't think it matters if you have the classes in the same file or not. Unity needs to know how to create the instance given the type.
If RegisterInstance is used, the specific object passed as argument is returned everytime Resolve is called for the type. If the type is registered using RegisterType (or not registered at all for concrete classes), Unity will try to instantiate the type by using the constructor with most number of arguments. For each of the parameter types, Unity will try to resolve them recursively.
Registering mappings for interface types to concrete types is mandatory but registering concrete types themselves is optional.
Sample code:
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);
}
}
}
Upvotes: 1
Reputation: 804
I always use the following code. When I load an application, the application looks in the directory for all Dlls. This way when you load a class with reflection it searches for the Dlls and exes. You can also add some more paths to search.
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;
}
Upvotes: 4
Reputation: 660
If you are looking for DI for the Unity3d engine, maybe this would work (I've not used it, but the feedback is positive) https://github.com/modesttree/Zenject
If you are talking about Microsoft's Unity DI library, you should be able to do this:
container.RegisterTypes(
AllClasses.FromLoadedAssemblies(),
WithMappings.FromMatchingInterface,
WithName.Default);
Upvotes: 8