Byron said:
I’m trying to come up with a way to selectively expose public methods (and
possibly properties) in a class within a DLL using .NET 3.5. I suspect this
is a fairly noobie question.
Well, inasmuch as a I suspect someone more experienced with OOP design
wouldn't ask it, sure.
Based on your description:
I have a class that can control several different serial devices. These
devices share some common methods such Start() and Stop() and some have other
specialized methods unique to them, like Pause() and Resume(). What I want
to be able to do is require the user of the class to supply the type of
device they are controlling, and restrict the public methods – and properties
- to those that are legal for the current device. There are hundreds of
public methods in the class, but no single device uses them all, so I want to
hide those that don’t apply to the current device type.
Typically, the way something like this would be managed would be to have
a base class that represents the common functionality, and then
subclasses that contain the specialized functionality.
For example, given:
Controller c = new Controller(DeviceType1);
c.Start(), c.Stop(), c.Pause(), and c.Resume() are visible
But when:
Controller c = new Controller(DeviceType2);
c.Start() and c.Stop() are visible but c.Pause() and c.Resume() are NOT
visible
Any suggestions would be greatly appreciated.
You cannot do things literally as you are asking. In a given class, a
member's accessibility is immutable. If it's ever public, it's always
public. So whatever is visible in the Controller type, will always be
visible in the Controller type.
What you can do is make Controller your base class, with Start() and
Stop() in it, and then have a sub-class that inherits Controller and
which declares the Pause() and Resume() methods.
In that approach, instead of using "new Controller(DeviceType1)", you'll
either have to instantiate the specific sub-class type explicitly, or
have a factory method (e.g. a static method in the Controller class)
that given a "DeviceType1", can figure out what the actual sub-class to
instantiate is and create and return an instance of that sub-class.
As an elaboration on that technique, if there is a sensible way to group
the various members that have this "optional visibility", you can
declare specific interfaces that have those members, and then have each
Controller sub-class implement the interfaces as appropriate to that
object. That way, rather than casting to the sub-class itself, you can
essentially query a given object for what functionality it supports by
checking for interface implementation using the "as" operator. This
would work particularly well with the factory method approach, because
otherwise the factory is kind of pointless, since you'd still have to
know the exact type being returned to get at the specialized members.
Here are some code examples illustrating the above:
class Controller
{
public void Start() { /* implementation here */ }
public void Stop() { /* implementation here */ }
}
class SpecificController : Controller
{
public void Pause() { /* ... */ }
public void Resume() { /* ... */ }
}
class Client
{
void SomeMethod(int type)
{
Controller controller = null;
switch (type)
{
case TypePlain:
controller = new Controller();
break;
case TypeSpecific:
controller = new SpecificController();
break;
// etc.
}
controller.Start();
if (type == TypeSpecific)
{
SpecificController specific = (SpecificContoller)controller;
specific.Resume();
specific.Pause();
}
controller.Stop();
}
}
or, alternatively:
interface IPlainController
{
void Start();
void Stop();
}
interface IPausableController : PlainController
{
void Pause();
void Resume();
}
class Controller : IPlainController
{
public void Start() { /* implementation here */ }
public void Stop() { /* implementation here */ }
public static Controller Create(int type)
{
switch (type)
{
case TypePlain:
return new Controller();
case TypeSpecific:
return new SpecificController();
// etc.
}
}
}
class SpecificController : Controller, IPausableController
{
public void Pause() { /* ... */ }
public void Resume() { /* ... */ }
}
class Client
{
void SomeMethod(int type)
{
Controller controller = Controller.Create(type);
controller.Start();
IPausableController pausable = controller as IPausableController;
if (pausable != null)
{
controller.Resume();
controller.Pause();
}
controller.Stop();
}
}
The latter is more complex in terms of implementation, but offers more
flexibility in usage. It allows the code to avoid special-casing
specific object types, and instead control execution flow based on the
features implemented by each controller type.
Regardless of how you approach the problem, there is always going to be
some difficulty dealing with a class hierarchy in which you have a
variety of classes with disparate features, but which you are trying to
use together and at the same time. Without knowing more about your
specific design goals, it's hard to offer better advice, but in general
it's easier to deal with the class design if when a sub-class has
specific features not found in the base class that you want to take
advantage of, you can write the code so that it always knows the type of
that sub-class.
Pete