Did you know: *Internal* interfaces on *Public* classes

By | June 14, 2013

So I’ll admit I’ve kind of been a more standard OOP developer. Today, however, I got a wild hair and did an F12 (Go to definition) on Tuple<t1, t2=””>. I found this:

public class Tuple<t1, t2> : IStructuralEquatable, IStructuralComparable, IComparable, ITuple

And thought “ITuple? What’s that?”

I went diving in to MSDN and found nothing.

So I looked at ITuple:

internal interface ITuple
{
    int GetHashCode(IEqualityComparer comparer);
    string ToString(StringBuilder sb);

    int Size { get; }
}

Sweet. An internal interface on a public class. Here is how you could make use of it:

interface InternalInterfaceTest
{
    bool InternalBooleanProperty { get; set; }
}

public class PublicClassWithInternalInterface : InternalInterfaceTest

And it works about as you’d expect:

interface InternalInterfaceTest
{
    bool InternalBooleanProperty { get; set; }
}

public class PublicClassWithInternalInterface : InternalInterfaceTest
{
    public bool InternalBooleanProperty { get; set; }
}

So in another assembly you can indeed do:

public class Class1
{
    PublicClassWithInternalInterface p;
    public Class1()
    {
        var i = p.InternalBooleanProperty;
    }
}

But if all this is true, how come you can’t do Tuple<t1, t2>.Size (a property shown on ITuple)?

image

Here’s what you’ll find if you look at Tuple<T1, T2>’s definition:

int ITuple.Size
{
    get
    {
        return 2;
    }
}

Notice the difference here: explicit implementation of the internal interface.

So I changed my example:

interface InternalInterfaceTest
{
    bool InternalBooleanProperty { get; set; }
}

public class PublicClassWithInternalInterface : InternalInterfaceTest
{
    bool InternalInterfaceTest.InternalBooleanProperty { get; set; }
}

And sure enough now in my other assembly, I can’t get to InternalBooleanProperty. I can’t cast to InternalInterfaceTest either, because… it’s internal to the other assembly. It’s also pretty cool that you can expose Interface members without letting the world know about the Interface using the first method I showed. Though I think that kind of goes against the point of an Interface ;)

From what I can see of this, it’d be a good way to ensure that your app talks to itself in the ways you expect if you are extending it internally, but don’t want to expose all your contracts to the whole world.

Interestingly enough while looking at Tuple<T>’s definition I also learned about IStructuralEquatable and IStructuralComparable. I plan on using these a bit more judiciously as a way to convey when I am indeed doing a structural comparison between two objects and not a reference comparison.