Dan
Dan

Reputation: 10690

Restricting public interface implementations to internal use only

I have an internal interface which inherits/extends a public interface:

internal interface IFoo: IList<string>
{
    void FooAdd(string item);
}

I only want to allow internal access to the members of IFoo and, by extension, IList<string>. Direct members of IFoo can be implemented with the internal keyword, but since IList<T> is public, I have to implement them explicitly:

public class MyClass: IFoo, IReadOnlyList<string>
{
    // IFoo implementations:
    internal void FooAdd(string item)
    {
        ...
    }

    // Explicit IList<string> implementations:
    void IList<string>.Add(string item)
    {
        ...
    }

    // Public IReadOnlyList<string> members:
    public int Count { get { return ... } }
}

Now, since the IFoo interface is internal, users of my lib cannot access the implemented methods that are declared internal in MyClass, which is what I intended. However, they can still access IList<T> members by casting, which I did not expect:

var myClassObj = new MyClass<string>();
(myClassObj as IList<string>).Add("Haha, gotcha!");

Outside the library, I would have expected that the compiler would complain that there is no way to cast myClassObj to an IList<string>, since MyClass only implements this interface through an internal interface.

Is there a different pattern I should use, if I want my class to implement public interface functionality, that should only be available internally?

Upvotes: 1

Views: 617

Answers (1)

Peter Duniho
Peter Duniho

Reputation: 70701

I would have expected that the compiler would complain that there is no way to cast myClassObj to an IList<string>

The compiler has no way to do that. Except in very limited scenarios, the compiler treats explicit casts and conversions deferentially, with the assumption that since you wrote it, you meant it. It's not hard to write code where a cast will always fail at run-time, even though the compiler allowed it and even though you could look at the code and see that it will always fail.

To see why, consider that your object could always be referenced by a variable of type object. When that happens, the compiler has no knowledge of the declared implementation of the object. It can't prevent a cast in the code, even if the cast is known to be illegal.

And of course, at run-time the only thing that matters is whether the object actually does implement the interface. So the cast isn't even illegal anyway. Explicit interface implementation doesn't hide the interface. It just imposes the requirement that the implementation can't be used except through an expression of that interface type. It's not a means of controlling accessibility at all.

If you don't want outside code to see your IList<T> implementation in an object that the outside code has access to, then you have to actually hide it from that code. The only way to do that is to change your design so that your object has an IList<T> instead of is an IList<T> (i.e. composition vs. inheritance), and that IList<T> is accessible only to the code you want (e.g. exposed by a property marked internal).

Do keep in mind that even then, accessibility is primarily a compile-time safety feature. Reflection in a full-trust environment (i.e. in the context the vast majority of managed code executes) will always allow access to whatever code has access to the object in the first place, regardless of accessibility. It's good to restrict access, for the purpose of encouraging correct usage of your types, but it's not a security feature per se (though it can be used in conjunction with such).

Upvotes: 1

Related Questions