1

When posting this question my hope was to find a way how to reference/point to an abstract class that that could be used by a worker/holder.

As the comments by @progman suggested and @laryx-decidua there is no way to hold a reference to an abstract class, but one can only hold a reference to a instantiated object.

Below you can find what I think is their proposed solution. To me this is an ugly solution, and I would have liked to have one that derives multiple static classes from an abstract base class and the holder gets references to those static classes to do its work. Deriving a static class form an abstract class Why you can't drive a static class is it seems prohibited by design and indicates bad architecture; although I don't see why the solution above is any better.

Suggested Solution

using System;
using System.Collections.Generic;

public abstract class BaseClass
{
    // Some declarative knowledge
    public int value;

    protected BaseClass(int value){
        this.value = value;
    }

    // Some procedural
    public abstract void execute();
}

public class ConcreteClass1 : BaseClass
{
    public ConcreteClass1() : base(42) {}

    public override void execute()
    {
        Console.WriteLine("In Concrete1! Value " + value);
    }
}

public class ConcreteClass2 : BaseClass
{
    public ConcreteClass2() : base(8888) { }

    public override void execute()
    {
        Console.WriteLine("In Concrete2! Value " + value);
    }
}

public class Holder
{
    BaseClass activeClass;
    public void setClass(BaseClass newClass){
        activeClass = newClass;
    }

    public void doWork()
    {
        int x;
        activeClass.execute();
        x = activeClass.value * activeClass.value;
        Console.WriteLine("Holder has done its work: " + x);
    }
}

class MainClass
{
    static void Main(string[] args)
    {
        List<BaseClass> classes = new List<BaseClass>();
        classes.Add(new ConcreteClass1());
        classes.Add(new ConcreteClass2());

        Holder holder = new Holder();

        holder.setClass(classes[0]);
        holder.doWork();

        holder.setClass(classes[1]);
        holder.doWork();

        holder.setClass(classes[0]);
        holder.doWork();
    }
}

producing

In Concrete1! Value 42
Holder has done its work: 1764
In Concrete2! Value 8888
Holder has done its work: 78996544
In Concrete1! Value 42
Holder has done its work: 1764
Manuel Pasieka
  • 151
  • 1
  • 1
  • 10
  • 1
    Simply use `Action` at your "MAGIC HERE" marker. – Progman May 25 '19 at 21:41
  • 1
    You can use the type of abstract classes like any other -- `Action current_action;`. At some point you'd need a non abstract class inheriting from `Action` though to be able to create an instance and assign that to `current_action`. But that somehow collides with your statement of " I don't want to instantiate any of them", which renders any field of the type `Action` pointless I guess. – sticky bit May 25 '19 at 21:44
  • 1
    hi @progman to my understanding Action is a specialized delegate (that is used to reference functions), but I don't want to wrap or reference a function but a class. – Manuel Pasieka May 25 '19 at 21:44
  • 1
    @ManuelPasieka It does. You just use the class name/type of your abstract base class. However, maybe you should rename it to something different than "Action" because `System.Action` already exists and it might be confusing. – Progman May 25 '19 at 21:48
  • @progman Doing something like Action current_action ```C# public set_action(BaseAction new_action) { current_action = new_action; } ``` I get an error saying that new_action is a Type and is not valid in this context. – Manuel Pasieka May 25 '19 at 21:54
  • 1
    @ManuelPasieka You have to replace `Action` with `BaseAction` as well. Please edit your question to include the full source code you have and the complete error message you get. – Progman May 25 '19 at 21:58
  • @progman I changed the code example to make the problem more clear. – Manuel Pasieka May 28 '19 at 14:54
  • @ManuelPasieka Again, replace the `Action` type with your now named `BaseClass` class/type. Then provide an object which extends from `BaseClass` as an argument for the `set_class()` method. The `WaitClass` class should most likely not be abstract, but that depends on what you want to do. As of right now, you currently don't have a non-abstract class which extends from `BaseClass` (but you need one). – Progman May 28 '19 at 18:09
  • @Progman, thx, I understand your solution, but my question is is this really necessary? Do i need those instances of the derived class? I am quite sure I don't understand enough the OOP background, but in the best of worlds, I would like the derived classes to be static and only switch between them in the worker. – Manuel Pasieka May 29 '19 at 11:01
  • @ManuelPasieka `static` is most likely not the solution (or problem) here. When you create an `abstract` class you must create sub classes from that class, which in the end of the inheritance-chain, which are not `abstract`. Keep in mind that "visibility" specified by the keywords like `private`, `internal` and `public` are unrelated to abstract and non-abstract classes. If you don't want a class to be usable/visible outside to other classes you might want to make them `internal` or `private`. You might need to learn about static/non-static, abstract/non-abstract and visibility. – Progman May 29 '19 at 18:02

1 Answers1

0

Disclaimer: I don't speak C#, but I suspect the situation is similar to C++. The rest of the answer is based on my C++ and general OOP experience.

If I understood you correctly, you'd like to hold derived class object(s) through a base class reference and invoke a polymorphic ("virtual") method on those objects. Because you expect those derived classes to be "stateless" (i.e. no data members), you thought maybe you could "get away with" static (and/or abstract) classes.

The problem is that you need to instantiate something to put into your Holder objects, because a reference (or a pointer) can refer to (point to) only to a concrete object. So you need to instantiate objects that will be referred to via a reference in Holder, as some of the commenters have already pointed out. That's why abstract classes won't do -- they cannot be instantiated.

If there were an OOP language that supports references to types , plus some mechanism that can do the following: "Hmm, here is a reference AnimalTypeRef to the (possibly abstract) base class type Animal. AnimalTypeRef actually refers to the derived class type Elephant. Now, the user wants to invoke a virtual method Animal::make_noise() that does not use any class state, so let's invoke the corresponding method Elephant::make_noise() that overrides it and returns this :-)." -- well, then you could do what you have asked for.

I suspect this has not been implemented in C++ or C# because there are not too many use cases for it, and actually the same thing can be done with the general mechanism that requires that references refer to concrete objects.

Just go ahead and derive concrete (non-abstract) classes from your abstract base class, and don't worry about their statelessness. It's perfectly OK to define and use concrete objects that have no data members. Instantiate them, then initialise a Holder object with them, using a reference to the (abstract) base class and that's it. Polymorphic method invocation through base class references will do the rest.

András Aszódi
  • 8,948
  • 5
  • 48
  • 51
  • Why do I need 'instantiate something' in order to be used by the Holder/Worker? I guess this is related to [Why you can't drive a static class](https://stackoverflow.com/questions/774181/why-cant-i-inherit-static-classes) although I don't see why doing so would be a bad idea either. – Manuel Pasieka May 29 '19 at 15:37
  • @ManuelPasieka Because by definition a reference refers to a concrete instance, an object in memory. A static class has no instances, it is not an object. You can complicate matters if you want by using the Singleton pattern -- but in my opinion it's not worth it. – András Aszódi May 30 '19 at 08:21
  • @ManuelPasieka Added some extra explanations for you, see updated answer. – András Aszódi Jun 03 '19 at 08:14