Preventing third-party derivation, part one
In this episode of FAIC, a conversation I had with a C# user recently. Next time, some further thoughts on how to use the CLR security system in this sort of scenario.
Him: I have this abstract class defined in one assembly:
// Assembly FooBar.DLL
public abstract class Foo
{
internal abstract void DoSomething();
// ...
}
I want to create a concrete class derived directly from Foo in another assembly, Blah.DLL.
Me: You are going to have to learn to live with disappointment.
Him: But I should be able to because I have public access to the class Foo!
Me: That statement is false. Having public access to a class absolutely does not mean that it is always legal to create a subclass in another assembly. There are any number of reasons why it might not be legal to create a subclass in another assembly; you happen to have run across just one of them. (There are plenty more -- for example, you could make a public class and mark all the constructors as internal. It's not possible to subclass that from another assembly either, since the derived class constructor does not have an accessible base class constructor that it can call.)
Him: You're right. In this case, I can’t provide an overriding implementation for the internal abstract method DoSomething() because it is not accessible. Therefore the compiler will give an error when I try to subclass Foo. How do I get around this?
Me: You don’t get around it. The type safety system is working as designed; don’t try to defeat it.
If you own the FooBar.DLL assembly then of course there are several ways you can do what you want. Two that immediately come to mind are (1) mark Blah.DLL as a friend assembly of FooBar.DLL using the InternalsVisibleTo attribute (2) change the accessibility of DoSomething to public, protected or protected internal.
Him: Why is it even allowed to have a nonextensible class like this in the first place?
Me: It is sometimes a good thing to make a class that cannot be extended arbitrarily. I have myself occasionally used a similar technique to ensure that no third party can subclass one of my public base classes, though, as we’ll see later, there are other, perhaps better ways.
Look at it this way: if the author of the class wanted you to be able to subclass it, they probably would have made it possible. Clearly they do not want you to subclass this class.
Him: My previous question has been thoroughly begged. Why would someone want to prevent a third party from subclassing?
Me: I can think of a few reasons. Here are two:
1) Designing a class to be extended effectively by third parties is work, and work requires effort. If the class is not intended to be extended by third parties, but must be unsealed (for internal extension, for example) then the implementer is faced with a choice: spend the time/money/effort unnecessarily, provide an extensible but brittle class, or prevent extension by some other method.
This trick is a cheap, easy and straightforward way to prevent extension by arbitrary third parties without preventing extension by internal classes. As we'll see next time, there are other ways you can do this too.
2) A class may need to be extensible internally and used externally, but must not be extended by third parties for security reasons. Imagine you have a method Transfer which takes two instances of your class BankAccount. Suppose BankAccount is an abstract class with two derived classes, CaymanIslandsAccount and SwissAccount. Do you really want arbitrary third parties able to make new objects which fulfill the BankAccount contract but were not implemented by you?
Again, you end up with a tradeoff – either implement Transfer so that it does type checks on the BankAccount passed to it (possibly expensive both in initial implementation and maintenance), implement Transfer so that it accepts any old thing (dangerous!), or prevent anyone from making an unknown kind of BankAccount (cheap, safe).
In general, good security design is “make them do something impossible in order to break the system”. (Or even better “make them do seven impossible things”.) By making it impossible for third parties to fulfill the contract, you add additional security to the system. (By no means enough security, but a little bit more.)
Him: Thanks, I see what's going on here. Clearly I got into this situation because this trick of using access modifiers to prevent extension is insufficient to convey the author of FooBar.DLL's intentions.
Me: Next time we'll talk about more obvious ways to state the intention of "please don't subclass this thing".
Comments
Anonymous
September 27, 2008
The comment has been removedAnonymous
September 27, 2008
>My previous question has been thoroughly begged ?? http://en.wikipedia.org/wiki/Begging_the_questionAnonymous
September 27, 2008
Right -- begging the question is answering a question in a manner which provides no new information, but rather, depends logically upon the answer to an equally difficult or more difficult question. Answering "Why is this allowed?" with "because it is desirable" doesn't tell you anything. Rather, it logically relies upon the answer to the harder question of "why is it desirable?" A similar example is attributed to Newton, in his criticism of the atomic theory. Why are some substances hard and some soft? Because all substances are made of these things called "atoms", and some atoms are hard and some are soft. Newton quite rightly pointed out that this was begging the question; it's answering the question in a manner that provides no new information and raises the same question at a different level.Anonymous
September 27, 2008
Joren: yes, you have re-stated my whole point here -- that if that is what the author of the class INTENDED, then they did a lousy job of communicating it.Anonymous
September 28, 2008
Today I have already learned of the InternalsVisibleTo attribute, and it's only 8am. Thanks Eric!Anonymous
September 28, 2008
FYI, InternalsVisibleTo is especially useful when you want to write unit tests for internal methods/classes as you usually do it from a separate dll.Anonymous
September 29, 2008
Maybe the whole thing points to a missing feature in most popular type systems. What did the author try to prevent: reusing a base implementation or adding new players to the game by implementing the type interface? If I remember well, C# requires same visibility to a type and its implemented interfaces. However, I think there are public types in mscorlib implementing internal interfaces (maybe I'm wrong in this). Should C# allow this situation? On the other hand, there's no easy way in C# to reuse an implementation without inheriting. I'm thinking in some alternative as interface delegation in good old Delphi.Anonymous
September 29, 2008
You do not remember well. C# does not require that an implemented interface be as accessible as the type. C# requires that a base class be at least as accessible as a derived class, but does not make the same restriction on interfaces. This is perfectly legal: public class C : C.I { private interface I {} } That is, C is a public class which implements private interface I. Similarly, this is perfectly legal: public interface I<X>{} public class C : I<C.D> { private class D {} }Anonymous
September 29, 2008
The comment has been removedAnonymous
September 29, 2008
<i>public class C : C.I { private interface I {} }</i> Heh, heh... that produces a "Circular base class dependency involving C and C.I" (Visual Studio 2008 SP1). I'm working in a compiler, and I have found the problem when importing type declarations from mscorlib, via reflection. Of course, I just ignore those "internal interfaces", since they don't have any effect on client code. In any case, it would be nice if C# allowed it, with internal nested types. A private interface would be of little use in these cases, imho.Anonymous
September 29, 2008
Whoops. As you point out, that is NOT legal. The reason I said it was legal is that I am actually fixing that bug as we speak, and on MY machine, it is legal. :-)Anonymous
October 01, 2008
what about using friend assemblies?Anonymous
October 01, 2008
What about them? Could you ask a more specific question?Anonymous
October 01, 2008
The comment has been removedAnonymous
October 01, 2008
The comment has been removedAnonymous
October 01, 2008
The comment has been removedAnonymous
October 02, 2008
Reduced accessibility isn't a security option, you could always get around it.. Don't even think about it this way :) I agree with Pop Catalins first paragraph, that attitude "i won't make this extensible beacuse i'm lazy", will definetly return later.. Such "lazy programming" has hidden costs in long-term development. Anyway, in my opinion (even little OT), one should use the interface almost everywhere, where is some base abstract class. Let's have an example: public abstract class FooBase { public abstract void DoSomething(); public virtual void DoSomething2(); public void DoSomething3(); protected void HelperMethod(); } Abstract method definitelly should be in iface, virtual method too, because the possible consumer can't rely on the default implementation (extension don't have to call the base). Helper method can't be consumed, and argument that 'it will be easier to extend that class' is wrong, you still can have base abstract class, with standard implementation etd., that implements iface, but consumer doesn't need to know about it. And at last, the problematic non-virtual method. I think, that consumer should never rely on the actual accurate implementation, because it's just hard to maintance and not flexible. Such method should be also declared by iface, that is, it could be virtual.. if it does sometning, that need's to be "closed", implement it as sealed in that easy-to-use abstract class. Sadly, C# alone does not encourage this pattern.. IoC does, and I love it :PAnonymous
October 02, 2008
The comment has been removedAnonymous
October 02, 2008
The comment has been removedAnonymous
October 02, 2008
The comment has been removedAnonymous
October 02, 2008
The comment has been removedAnonymous
October 12, 2008
The comment has been removedAnonymous
October 16, 2008
Just wanna highlight an issue with the mentioned approach, because you may make your customers to hate you just because you did not provide them with any point of extensibility. For instance, in Silverlight, there is a number of specialized collections derived from the abstract generic collection System.Window.PresentationFrameworkCollection<T>. Some of them are public, some of them internal, but all of them are sealed. At the same time, the PresentationFrameworkCollection<T> has few abstract internal methods, those make you unable to derive your own collection from this one. Just because of this, the customer should spend much more time/efforts/money on reinventing the wheel by creating the same functionality in his/her own code. So, you have to always remember about your customers, even if there would be just one single customer - you. Because the requirements are going to change, so the software changes follow the requirements. You better be prepared for that with your code structure, then in a future trying to find any workarounds for the limitations you put in yourself.Anonymous
October 16, 2008
He-he... Seems that everybody is complaing about that the mentioned approach is violating an open/close principle. Wouldn't it be more valuable to tell about how to follow open/close, then how to violate it?Anonymous
October 16, 2008
He-he... Seems that everybody is complaing about that the mentioned approach is violating an open/close principle. Wouldn't it be more valuable to tell about how to follow open/close, then how to violate it?Anonymous
October 19, 2008
You can actually create a concrete subclass of an abstract type with an internal constructor. Just not in C#. Basically in IL you would define your new constructor with a body that initializes whatever you need but does not call any superclass constructor directly. Instead you can use reflection if you really need to invoke that superclass constructor. This works because the CLR does not check that a subclass actually calls the constructor of its superclass.Anonymous
October 25, 2008
well.. accessibility levels are there so that you can structure your code in a way that prevents you and others from trying things you did not plan to be done by anyone. i use them to make sure that one thing (i.e. setting a property value) can only be done in one way (by making the backing field private) so that later when i add functionality (i.e raising an event) to the setter, i can be sure that neither i or someone else did go a way i did not expect. all in all you reduce the number of ways one can go and thus the number of ways something you did not think of could happen. oh well.. and i make ugly stuff private /internal so no one can see it ;)