Tuesday, February 10, 2009 10:47 AM
One of these languages is not like the others - Part 1 - Enforcement of Abstract Implementation
This is the first in a series of posts on the topic of interaction between different .NET languages. I will cover all of the major Microsoft languages: C#, Visual Basic, F# and C++/CLI.
In this first post in the series I will build a four language project in Visual Studio 2008 and begin to explore inter-language inheritance. One of the languages behaves in a significantly different way when compared with the others, can you guess which one it is?
Enforcement of Abstract Implementation
First things first, let’s define our abstract base class. C# is considered the standard language for library architecting and so it is what we will be using.
1: public abstract class CSharpBaseClass
3: public abstract void PublicAbstractMethod();
4: public abstract void PublicAbstractMethod(int inint);
5: protected abstract void ProtectedAbstractMethod();
6: protected abstract void ProtectedAbstractMethod(int inint);
In each of our child projects we will be inheriting from this class. Let’s start with C#.
1: public class CSharpChildClass : CSharpBaseClass
Now anyone with even a bit of experience in the C# language knows that this won’t compile. For each of the abstract methods in the base class the inheriting class is expected to provide an implementation.
… error CS0534: 'CSharpChild.CSharpChildClass' does not implement inherited abstract member …
This is similarly true for Visual Basic.
1: Public Class VBChildClass
2: Inherits CSharpBase.CSharpBaseClass
3: End Class
… error BC30610: Class 'VBChildClass' must either be declared 'MustInherit' or override the following inherited 'MustOverride' member(s):
F# shares this behavior as well.
1: type FSharpChildClass() =
2: inherit CSharpBase.CSharpBaseClass()
… error FS0191: No implementation was given for 'CSharpBaseClass.PublicAbstractMethod() : unit'.
Kudos to the F# compiler designers for the verbose compile time errors.
error FS0054: This type is 'abstract' since some abstract members have not been given an implementation. If this is intentional then add the '[<AbstractClass>]' attribute to your type.
However, C++/CLI does not. It’s perfectly acceptable to inherit from an abstract class and provide no implementation for it’s abstract members.
1: public ref class CppChildClass : public CSharpBaseClass
While a warning is issued, it seems insufficient for what has just taken place:
… warning C4570: 'CppChild::CppChildClass' : is not explicitly declared as abstract but has abstract functions
The result is that the class in question is defaulted to abstract. If someone where to attempt to instantiate it downstream, it would cause a compiler error:
1: CppChildClass cpp = new CppChildClass();
error CS0144: Cannot create an instance of the abstract class or interface 'ManagedCppChild.ManagedCppChildClass'
This is bad because, if you were an API developer and so did not consume all of your objects, it would be possible to ship a version of your product with abstract suddenly toggled on for one or more of your classes. All it would take would be for a developer to add a new inherited class or member to an existing abstract base class without writing a corresponding test for every abstract member or inheriting class.
So, what can we learn from this? It seem that when using C++/CLI (or managed C++) it is not sufficient to depend on abstract classes to enforce that members are implemented. Testing must also be strictly enforced.