How can I use Unity with internal classes?

2019-02-23 22:38发布

问题:

I have a Web API application and am using Unity for dependency injection. The application uses a library containing an Interface IDoStuff and a class that implements the interface:

internal interface IDoStuff
{
    void DoSomething();
}

internal class DoStuff : IDoStuff
{
    public void DoSomething()
    {
        // Do some stuff
    }
}

The library also has an public class that needs to do stuff:

public class NeedToDoStuff
{
    private IDoStuff doStuff;

    public NeedToDoStuff()
    {
        this.doStuff = new DoStuff();
    }

    internal NeedToDoStuff(IDoStuff doStuff)
    {
        this.doStuff = doStuff;
    }

    void PleaseDoStuff()
    {
        doStuff.DoSomething();
    }
}

I'd like to use Unity to create a NeedToDoStuff instance in my controller and to also be responsible for creating the DoStuff class internally, rather than the constructor calling new directly. However, the only way I can see to do this is to make both the IDoStuff interface and DoStuff class public which seems wrong to me. It seems wrong as these are implementation details and only relevant within the library itself. I get that with inversion of control you're allowing the top level application to make choices about its underlying implementations via configuration of some sort but should this mean that there's no longer a need for internal etc?

回答1:

Forget for a moment that you are using a container and let's say that you create those classes yourself in the startup path of your application. How would you actually do this in C#?

The answer is, you can't. This will simply not compile in C#, because C# requires those types to be public. So although those types might be an implementation detail to other components, they are not an implementation detail to the part of your application that wires them together. Whether or not you use a DI library to help you is irrelevant; that library needs access to those libraries, because to the library, those classes are not implementation details.

And do note that there are different ways of hiding those classes. You can move the interfaces into their own assembly and let both the consuming library and the library that contains the implementations depend on that new 'contract' assembly. When you don't let the consuming assembly depend on the implementation assembly, the implementation types are effectively hidden from the consuming assembly, even though those types are still public.



回答2:

While I am personally against internal interfaces you can allow unity to use them by adding

[assembly: InternalsVisibleTo("Unity_ILEmit_InterfaceProxies")] 

to the AssemblyInfo.cs file of the project containing your interfaces. I found this after attemping to add [assembly: InternalsVisibleTo("Microsoft.Practices.Unity")] which I have seen not work in other post and had no effect, but felt it was the right direction. The stacktrace I pulled from my code referenced the assembly above and allowed the code to work.

This would allow you to hide your Interfaces. The constructor is another story though.



回答3:

I've posted my own answer as I believe it comes closest to answering my original question. It may not be as clean a design choice as Steven's recommendations but love it or hate it, it does allow for the internal classes of a library to be used in conjunction with Unity.

I needed to make three updates to the classes/interfaces:

  1. Make the interfaces public.
  2. Make the constructors public.
  3. Introduce a static class/method that performs unity registration within the library that is called from the main unity registration logic performed on application start up.