タイトルには「Circular Dependency」と書かれていますが、それは正しい表現ではありません。私にはデザインがしっかりしているように見えるからです。
ただし、次のシナリオを検討してください。青い部分は外部パートナーから提供され、オレンジは私自身の実装です。またConcreteMain
、複数あると仮定しますが、特定のものを使用したいと思います。(実際には、各クラスにはさらにいくつかの依存関係がありますが、ここでは単純化しようとしました)
すべてをDepency Injection(Unity)でStackOverflowException
インスタンス化したいのですが、明らかに次のコードを取得します。これは、RunnerがConcreteMainのインスタンス化を試行し、ConcreteMainにはRunnerが必要だからです。
IUnityContainer ioc = new UnityContainer();
ioc.RegisterType<IMain, ConcreteMain>()
.RegisterType<IMainCallback, Runner>();
var runner = ioc.Resolve<Runner>();
どうすればこれを回避できますか?これをDIで使用できるように構成する方法はありますか?私が今しているシナリオは、すべてを手動で設定することですが、ConcreteMain
それをインスタンス化するクラスに強い依存関係を置きます。これは私が回避しようとしているものです(構成にUnityの登録がある場合)。
以下のすべてのソースコード(非常に単純化された例!);
public class Program
{
public static void Main(string[] args)
{
IUnityContainer ioc = new UnityContainer();
ioc.RegisterType<IMain, ConcreteMain>()
.RegisterType<IMainCallback, Runner>();
var runner = ioc.Resolve<Runner>();
Console.WriteLine("invoking runner...");
runner.DoSomethingAwesome();
Console.ReadLine();
}
}
public class Runner : IMainCallback
{
private readonly IMain mainServer;
public Runner(IMain mainServer)
{
this.mainServer = mainServer;
}
public void DoSomethingAwesome()
{
Console.WriteLine("trying to do something awesome");
mainServer.DoSomething();
}
public void SomethingIsDone(object something)
{
Console.WriteLine("hey look, something is finally done.");
}
}
public interface IMain
{
void DoSomething();
}
public interface IMainCallback
{
void SomethingIsDone(object something);
}
public abstract class AbstractMain : IMain
{
protected readonly IMainCallback callback;
protected AbstractMain(IMainCallback callback)
{
this.callback = callback;
}
public abstract void DoSomething();
}
public class ConcreteMain : AbstractMain
{
public ConcreteMain(IMainCallback callback) : base(callback){}
public override void DoSomething()
{
Console.WriteLine("starting to do something...");
var task = Task.Factory.StartNew(() =>{ Thread.Sleep(5000);/*very long running task*/ });
task.ContinueWith(t => callback.SomethingIsDone(true));
}
}