Static Abstract methods? Why not?

  • Thread starter Thread starter Mike Ruane-Torr
  • Start date Start date
Thanks to everyone who replied and explained this to me. I now realise
that what I was trying to do doesn't actually have a use.

I will now implement this part of my design with non-static, abstract
virtual methods. My intention was to enable the user to select a
particular inherited class at run-time, in order to control behaviour.
To this end, I wanted each class to supply a description of what it
does, so that I could populate a drop-down - but I didn't want to
instantiate anything at that point, because of the overheads that might
imply.

I thought I might just add an update to this thread, because I am
beginning to discover that this design pattern is needed more frequently
than I realised. I have just come up against another one!

I have a base class (call it B), a set of derived classes (call them Dn,
where n is a number), and a set of classes derived from them (call them
Dns, where s is a parameter, controlling the 'style' of object created -
this is in relation to a UI design).

I want a static method in Dn that takes an enumerated type as an argument
controlling the required style, and returns an instance of a Dns class
according to the style. So my method would look similar to this:

public class Dn : B
{
 
There is nothing horribly wrong with it, however, there are a few points
which severely reduce its attractivness.
1) abstract is a bad term, it wouldn't be an abstract method in the sense
that abstract methods mean in every other circumstance. There is no
virtuality here. To add this would require adding a fair amount of thought
to the languages involved(VB, C++, and C# at the least, otherwise the
feature would be fairly close to useless), likely require some changes to
the runtime, and potentially, possibly ideally, result in additional
keywords. As a feature it needs alot of thought and, IMHO, if added at all
it should be added as a smaller part of a larger set of features with a
common goal(you could call it type\component level contracts, I suppose,
instead of implementation level like interfaces are), as on its own it lacks
a great deal.
2) Because there is no way to directly call a static variable based on the
instance type, the scope of a feature is really is limited to reflection,
which in and of itself makes the feature far less pervasive on its own.
Something like an fxcop rule or another static analysis tool would do the
job easily without adding complexity to the language, compilers, and
runtime.

Now, that isn't to say that the idea has no merit, simply that without alot
of work and consideration it would likely end up being a bit of a farce,
messy and unusable. More importantly a syntax to, in a sense,
polymorphically call a static method without using reflection(directly
anyway) would be needed. it would require some changes in method call and
propery get\set syntax and it would be something that required alot of
energy to get right, if it is possible to get it right.

Now, as for your reasons for wanting this feature in this case, I'll let
someone else comment more fully(someone probably will). It sounds to me like
you need to use the factory pattern, but alas it is already after 2 and I
have a great deal to finish before sleep.
 
Mike Ruane-Torr said:
I thought I might just add an update to this thread, because I am
beginning to discover that this design pattern is needed more frequently
than I realised. I have just come up against another one!

-snip-

Hi Mike - I have been wrestling with this issue recently. I have
attached a longish example solution that works for me but I'm not sure
if it will pass the "good design" test. I haven't been able to get
much in the way of feedback.

I use the CommandFactory to create an appropriate command to process a
string, but the string format isn't known beforehand and I want each
type of command to determine if it can process the string. Obviously I
don't want to instantiate each command just to find out if it can
process the input.

My CommandFactory caches the method info for CanProcess(), the static
method used to determine if the command can process the input. I have
an abstract base Command from which all other Commands derive. Each
derived command must "hide" the CanProcess() method in the base
Command. If a derived Command fails to do this then the CanProcess()
from the base Command will be called. If the command can process the
input a new instance of the command is returned from CanProcess().

I can't see too much in the way of downside, caching the method info
should avoid performance issues associated with using Reflection. New
commands can be added without difficulty. Any comments would be
appreciated. I've wondered whether delegates might be more appropriate
but I haven't yet been able to figure out how to apply them
appropriately to this situation.

I hope this might be of some value.

Dave


using System;
using System.Text;
using System.Reflection;

namespace Reflect
{
abstract class Command
{
// this method will be invoked if derived class fails to
// provide a "new" version because of
// "BindingFlags.FlattenHierarchy", always returns null
public static Command CanProcess( string Message)
{
Console.WriteLine("Command.CanProcess()");
return null;
}

protected string message;
protected Command( string Message) { message = Message; }
public abstract string Process();
}

class BalanceCommand: Command
{
// "new" keyword required to override base method
public new static Command CanProcess( string Message)
{
Console.WriteLine("BalanceCommand.CanProcess()");
if (Message == "BAL")
return new BalanceCommand( Message);
else
return null;
}

public BalanceCommand( string Message) : base( Message) { }
public override string Process() { return "BalanceCommand"; }
}

// UnknownCommand is a Special Case(496), Patterns of Enterprise
// Application Architecture
class UnknownCommand: Command
{
// catch all method always returns new Command
public new static Command CanProcess( string Message)
{
Console.WriteLine("UnknownCommand.CanProcess()");
return new UnknownCommand( Message);
}

public UnknownCommand( string Message) : base( Message) { }
public override string Process() { return "UnknownCommand"; }
}

class BadCommand: Command
{
// BadCommand has no implementation for canProcess, base method
// will be invoked because of "BindingFlags.FlattenHierarchy" in
// GetMethod call
public BadCommand( string Message) : base( Message) { }
public override string Process()
{ return "BadCommand (should never be called)"; }
}

sealed class CommandFactory
{
private CommandFactory(){} // disable ctor

// method info cache
private static MethodInfo [] methods = null;

// UnknownCommand should always be last value in array
private static string [] commands = {
"BalanceCommand",
"BadCommand",
"UnknownCommand"
};

public static Command Create( string Input)
{
Command cmd = null;
Object [] input = { Input };
foreach (MethodInfo m in Methods)
{
cmd = m.Invoke( null, input) as Command;
if (cmd != null) break;
}

return cmd;
}

private static MethodInfo [] Methods
{
get
{
if (methods == null)
{
int commandCnt = commands.Length;
methods = new MethodInfo[ commandCnt];
Type [] paramTypes = { typeof( string) };
for (int i=0; i < commandCnt; i++)
{
Type t = Type.GetType("Reflect." + commands[ i]);
methods[ i] = t.GetMethod( "CanProcess",
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.FlattenHierarchy,
null,
paramTypes,
null);
}
}

return methods;
}
}
}

class Class1
{
[STAThread]
static void Main(string[] args)
{
Object [] msg = {"BAM"}; // message text to be processed
Command cmd = CommandFactory.Create( "BAM");
Console.WriteLine( cmd.Process());
}
}
}
 
Back
Top