Looking for a way to hide a class' base class
I'm building a library where I've got a p开发者_StackOverflow中文版attern like the following:
public class Foo : Foo_Impl
{
}
public class Foo_Impl
{
}
I don't want other developers to accidentally use the Foo_Impl class. What options are available to me to hide this? I'd also like to hide it from other classes in the same assembly it's defined in. Ideally I'd have loved to do:
public class Foo : Foo_Impl
{
private class Foo_Impl
{
}
}
But that doesn't work for a variety of reasons.
I haven't seen this suggested yet, so I thought I would add my $.02.
How about using composition instead of inheritance? An instance of Foo_Impl can be maintained by Foo and will never be visible to the outside world (because Foo_impl will be private to the assembly). Calls can be passed through the interface to Foo_Impl functions as needed. You get the same functionality and none of the design headaches.
private class Foo_Impl
{
public void DoSomething() { }
}
public class Foo
{
private Foo_Impl _implementation;
public Foo() { _implementation = new Foo_Impl(); }
public void DoSomething() { _implementation.DoSomething(); }
}
As far as "hiding it from other classes in the assembly", you could make it a nested class if you really felt that was appropriate.
Make Foo_Impl
an abstract class. This won't stop other developers from deriving from it, but it will make it impossible to create an instance of Foo_Impl
directly - it will need to be instantiated by creating a derived object, such as Foo
.
public abstract class Foo_Impl
{
public Foo_Impl()
{
}
}
public class Foo : Foo_Impl
{
public Foo() // implicitly calls the base class's constructor
{
}
}
-
var works = new Foo(); // Works
var error = new Foo_Impl(); // Compiler error
Per Thomas Levesque's suggestion, you also have the option of making the abstract constructor internal:
public abstract class Foo_Impl
{
internal Foo_Impl()
{
}
}
public class Foo : Foo_Impl
{
public Foo() // implicitly calls the base class's constructor
{
}
}
This will prevent developers from inheriting from Foo_Impl
from outside the assembly.
There is no way to completely hide it. If Foo
is going to be public, its base class has to be public too. However, you can prevent other developers from creating an instance of Foo_Impl
, by making the constructor protected. That way the constructor can only be used from derived classes.
public class Foo_Impl
{
protected Foo_Impl()
{
}
}
public class Foo : Foo_Impl
{
public Foo() // implicitly calls the base class's protected constructor
{
}
}
If you don't want people to be able to create their own classes derived from Foo_Impl
, make the constructor internal
The options you have are:
Make Foo_Impl abstract. Developers will not be able to instantiate a Foo_Impl directly; they must declare a Foo. However, Foo_Impl can still be used as a method parameter or generic type parameter, and developers can derive their own classes from Foo_Impl.
Make Foo_Impl internal, and place both it and Foo in a seperate library from any other class you want to hide Foo_Impl from. Developers working outside that assembly will not be able to see Foo_Impl. However, developers may be able to reflect the type and instantiate one if they have sufficient CAS permissions in the runtime, and any developer with access to your assembly's source can new up a Foo_Impl within that assembly.
Make all constructors of Foo_Impl protected or internal. Protected constructors can only be accessed by derived classes, while internal constructors can only be accessed from within the same assembly. Either way, a Foo_Impl cannot be instantiated by an outsider. However, developers can still use the class as a parameter or generic type.
Mark Foo_Impl's constructors, or the whole class, as Obsolete using the [Obsolete] attribute, specifying that use of this class should trigger an error. It's kind of a hack, but you can both prevent compilation of "wrong" code, and use the Message parameter of the attribute to specify what SHOULD be used. However, take care what you decorate; if you decorate the entire class, I do not think the compiler will let you do ANYTHING with the class, including inherit from it. Decorating the constructors ought to stop most coders.
This might sound silly, but there's a very simple solution: document the correct usage. If Foo_Impl shouldn't be instantiated, then add a comment to the class saying so. Include it in any other documentation about the class, as well.
You can (and should) do this even if you implement one of the other solutions here.
The abstract keyword is probably the most applicable to your scenario. Bear in mind that if you are providing an assembly (dll) that you want people to take advantage of a particular bit of functionality, you can implement the black box paradigm via Interfaces. If you provide the interface to be used and internally implement the interface, your assembly can provide the interface reference without handing over a reference to the instantiated class itself. All the developers would know are the properties and methods you've defined in the interface definition. This is highly common in plugin architectures.
Ok, I think I have solved the riddle. You implement Foo_Impl in two partial parts.
One is public partial class Foo_Impl {}
implementation which is basically an empty class doing nothing, so it will not be usable in any sense.
The other is implemented as a private partial class Foo_Impl{}
member of the Foo:Foo_Impl
class. Thats where you put all your secret functionality that can only be used by Foo itself. Any class can inherit from the public partial part of Foo_Impl
but cannot use any of the private functionality only available to Foo through its private Foo_Impl memeber. An example class Voyeur:Foo_Impl
is used to illustrate that every body can inherit but only Foo can instantiate and use it. Voyeur
cannot peep into Foo_Impl to get a glimpse of its useful private parts ;) .
using System;
static class Program
{
static void Main(string[] args)
{
new Foo();
new Voyeur();
Console.ReadLine();
}
}
public partial class Foo_Impl
{
// NOTHING in this public partial implementation.
// Visible but cannot be used for anything
protected Foo_Impl()
{
Console.Write("public partial Foo_Impl's protected constructor ");
}
}
public class Foo : Foo_Impl
{
private partial class Foo_Impl {
// put all your secret implementations here that you dont want
// any other class to accidentally use (except Foo)
public int fooAccessibleVariable=42;
public Foo_Impl ()
{
Console.Write("private partial Foo_Impl contructor ");
}
}
public Foo():base() // calls the public partial Foo_Impl's construtor
{
Console.WriteLine(" called from Foo()");
Foo_Impl myFoo_Impl = new Foo_Impl(); // calls the private partial Foo_Imp's constructor
Console.WriteLine(" called from Foo()");
Console.WriteLine("private Foo_Impl's variabe thats only accessible to Foo: {0}",
myFoo_Impl.fooAccessibleVariable);
}
}
class Voyeur:Foo_Impl
{
public Voyeur():base()// calls the public partial Foo_Impl's contructor
{
Console.WriteLine(" called from Voyeur()");
}
}
精彩评论