On InternalsVisibleTo
Some people pointed out that the distinction between public and published can be done using InternalsVisibleTo. This is sort of possible, I agree, but it only works if you think about this as a unit testing measure.
Jon Skeet asked about good usages of InternalsVisibleTo aside from unit testing, and I decided to check and see what the framework is using it for. From cursory observation, it appears to be heavily misused. Just from observing the allowed dependencies make me cringe.
- System.Data allows:
- System.Data.Entity
- SqlAccess
- System.Data.DataSetExtensions
- System.Web allows:
- System.Web.Extensions
- System.Xml allows:
- System.Data.SqlXml
- System.Data.SqlXml allows:
- System.Xml <-- I don't want to know how they got this cycle
- Microsoft.NETCF.Tools allows:
- System.Web.Services <-- This one really scares me
- Microsoft.Office.Tools.Common.v9.0 allows:
- Microsoft.Office.Tools.Word.v9.0
- Microsoft.VisualStudio.Tools.Office.Designer.Office2007
- Microsoft.VisualStudio.Tools.Office.Designer.Office2007Tests
- Microsoft.VisualStudio.Tools.Office.Outlook.UnitTests
- Microsoft.Build.Conversion allows:
- Microsoft.Build.Conversion.Unittest
- Microsoft.Build.Engine allows:
- Microsoft.Build.Engine.Unittest
- PresentationCore allows:
- System.Windows.Presentation
- PresentationFramework allows:
- PresentationFramework.Luna
- System.Core allows:
- Dlinq.Unittests <-- this is annoying, I don't get internals access to that, and I am also writing a linq provider
- System.Design allows:
- System.Web.Extensions.Design <-- well, if I want to write a designer, I have better work for MS...
Comments
InternalsVisibileTo is also a PITA to use with signed assemblies :s
FYI, you can use NDepend to re-engineer the InternalsVisibleTo usage.
This CQL query tells you about internal methods that are used outside their assembly.
SELECT METHODS WHERE ShouldBePublic
1010 methods on the .NET Fx 3.5 (including XPF WCF...)
This one tells you about assemblies tagged with this attribute:
SELECT ASSEMBLIES WHERE HasAttribute "System.Runtime.CompilerServices.InternalsVisibleToAttribute"
37 asm on 112
Patrick,
If I had a hat, I would have taken it off.
I've been thinking about the potential to use this to get around the requirement that Linq queries can only be constructed against the public members of domain objects.
This goes against the push for behaviour-rich objects by encouraging the developer to expose more public properties on domain objects (so that code in other assemblies can construct queries on those properties.)
I like the way that mapping to fields with NHibernate allows you to query based on the mapped representation of an object in HQL while maintaining encapsulation.
Perhaps by making the mapped properties 'internal' and visible to the data access/repository assemblies only, we can get some of this capability back...?
IMHO InternalVisibleTo should be used only for unit testing purposes to avoid certain reflection malabarism. For the rest of the stuff I prefer to think it does not exists.
@Frederico,
there are better approaches than using InternalsVisibleTo, such as the "publish through interface" way that ayende wrote about yesterday
it shouldn't be used at all ;)
I agree that it is testing-only. However, I am still not sure what advanced functionality may use "public but not published" features.
By the way, a kind of "public but not published" may be achieved by [EditorBrowsable(...None)]. If language supported Obsolete-like attributes in an abstract way, it would be also be possible to make a warning attribute.
To those railing against InternalVisibleTo being used for anything other than unit tests, and to those railing against it being used at all, it is worth pointing out that there are situations in which multiple-assembly development is required and references across those assemblies are also required, but in which a clear distinction (and degree of defense) is needed between the things usable across assemblies but only within the organization and things usable by the public at large.
I'm running into this presently in a project involving the creation of a framework and suite of tools to support a standards development effort, where the framework will be extended and used by the public at large, and achieving the goal described above is (so far) only practically achievable through the use of internal with InternalsVisibleTo.
To be clear, I do cringe a bit at having to use this (especially with its magic strings), but the .net framework doesn't seem to offer a better alternative.
@Davy
You assume some degree of control on the source code that sometimes is not possible. But your point is worthy anyway.
InternalsVisibleTo has bitten me badly in the past. Take as another example Microsoft.VisualStudio.QualityTools.Common that has InternalsVisibleTo 160 different Microsoft assemblies. This assembly contains all the code for running unit tests. So unless you work for Microsoft, you cannot host Microsoft's unit testing framework. This means that you're left running mstest.exe using System.Diagnostics.Process and parsing the resulting XML output, which you don't get until the end of the run. If you're writing a unit testing GUI, you have to pick between launching lots of MSTest instances (SLOW!) so that you can report progress or one MSTest instance without progress reports.
@James - Something bit you, but I'm not sure it was MSFT's use of InternalsVisibleTo. Probably more like the lack of a public programmatic API for MSTest, which is a distinct issue of one stops to think about it. ;)
If they've marked those things internal and used InternalVisibleTo, it is because they need to use it across their assemblies but specifically don't want anyone outside of their organization taking dependencies on what would be interpreted as public, stable and supported.
I used to get just as ticked at MSFT as everyone else, with "why is this internal!", "why is that sealed!", "why isn't this an interface instead of or in addition to an ABC!", etc. but having moved from my last few projects which were publicly-exposed in function but never in "code" (either as source or binaries) to one that will be very much exposed I'm starting to get a better feel for what it might be like on the MSFT side of the fence in this kind of scenario. I don't envy them one bit.
All that said, I'd still love to hear from anyone with an alternative to InternalsVisibleTo. And, no, marking things public does not count. Neither does extracting new things that are marked public. :D
Jeremy,
I am working on several projects which expose API.
This approach has served me well enough there. I don't agree that is doesn't count
I've already replied to Oren via email, so here is a recap / an alternate take / some further thoughts:
We are already having to mark more things public than we want to in order for DynamicProxy to be able to proxy them during mock object generation. I can live with this because of the net win provided by mocks, but do fear that more and more things will be dragged public as a result. Every type that gets dragged public is going to have to be specifically documented as not being for public use and I am going to have to answer to the people that want to know why these things have to be documented as such instead of being completely hidden.
I'm now absolutely sure I know how the Softies must feel about this. Marking yet more things public is a non-option regardless of how many people tell us that we should just go ahead and do it. Telling us more times doesn't make it any more of an option.
I would REALLY love to hear of an alternative to using InternalsVisibleTo to at least reduce the number of things being dragged public. So far I haven't. :(
After a bit of surfing, I have re-discovered the fix for the DynamicProxy issue, which I read about ages ago but forgot about, so suffice it to say that that particular problem will be cleaned up in my codebase within the next few days, leaving only the magic strings smell and the violent cries of a select few. ;)
namespace MyApp.Internal
{
// whatever
}
You explicitly mark some things as non of your business. This gives the client the ability to deal with things, without having to cause so many issues with internals.
I will quite likely do just that for those things that do in the end get dragged public, as much as I'll be working to minimize that wherever possible.
Comment preview