views:

242

answers:

4

What is the best way to implement a strategy for the constructor of a template/abstract class in C#? I have several classes which are all based on parsing a string inside the constructor. The parsing is done in a static method which creates list of key value pairs and is common for all classes, but some fields are also common for all classes - thus I use a abstract template class.

The problem is that I do not see a way to inherite the implementation of the constructor of the abstract base class. Otherwise I would implement the constructor strategy in the base class and would force the handling of the lists inside some abstract methods.

Edit: Added Not working code for the template class

public abstract class XXXMessageTemplate 
{
    public XXXMessageTemplate(string x) // implementation for the constructor 
   {
       Parse(x);//general parse function
       CommonFields();//filling common properties
       HandlePrivateProperties();//fill individual properties
       HandlePrivateStructures();//fill individual structures
    }
    abstract void HandlePrivateProperties();
    abstract void HandlePrivateStructures();
}

The actual messages should not implement any constructor and only implement the HandlePrivateProperties and HandlePrivateStructures functions.
+7  A: 

If you want the logic of the base class constructor to run in the derived class, you'd normally just call up to it:

 public Derived(...) : base(...)
 {
     // ...
 }

The base class can call abstract/virtual methods during the constructor, but it's generally frowned upon as the derived class's constructor body will not have been executed yet. (You'd want to document this really emphatically.)

Does this answer your question? I'm not entirely sure I understand the issue - some pseudo-code would help.

EDIT: The derived classes have to implement constructors. Constructors aren't inherited. If you don't specify any constructor, the compiler will provide a parameterless constructor which calls a base parameterless constructor. However, you can easily write a constructor with the same signature and just call the base class constructor:

public Derived(string x) : base(x)
{
    // Base constructor will do all the work
}
Jon Skeet
I have added some pseudo-code to make the issuer cleaner...
weismat
Ok - this means my intension is impossible to force the strategy pattern here - I have to implement then a constructor which calls a protected function name constructor which calls then the Handle functions. Reminds me of the restriction that a constructor can no call another constructo of a class.
weismat
Constructors can call constructors of other classes - you can write "object o = new object()" for instance. You can't try to use another constructor as part of your own constructor chain though - that wouldn't make much sense.
Jon Skeet
Another option would be to encapsulate the strategy in a separate interface, and pass that interface into the constructor - don't bother with the derived classes at all.
Jon Skeet
+2  A: 

Provide a constructor for the base class and use it in the derived classes:

abstract class Base {
     // ...
     protected Base(string commonField) {
        CommonField = commonField;
     }
}

class Derived1 : Base {
     public Derived1(string commonField, string specificField) : base(commonField) {
        SpecificField = specificField;
     }
}
Mehrdad Afshari
+1  A: 

I am not 100% sure I understand the question fully, but do you mean that you want your subclasses to pass a literal string to the base, as in this example?

public class MyMessage : XXXMessageTemplate
{
    public MyMessage() : base("MyMessage String")
    {
    }

    public override void HandlePrivateProperties()
    {
        // ...
    }

    public override void HandlePrivateStructures()
    {
        // ...
    }
}
Paul Ruane
+1  A: 

As I can see the problem is in Parse(...) method. Not in the method itself but in his existence. You have some raw data (string x) which must be converted into structured data (key value pairs) before use to construct objects. So you need somehow pass structured data into base and child constructors. I see 3 approaches:

  1. Parse data in base class and use protected base property to pass it to childs.
  2. Parse data before calling constructor.
  3. Parse data in place of usage.

1 You may extend an Mehrdad answer via additional protected property which holds parsed args. Something like:

abstract class Base {
     protected ParsedData ParsedData;
     // ...
     protected Base(string x) {
        ParsedData = Parse(x);
        CommonFields(); //initialize common fields using ParsedData
     }
}

class Derived1 : Base {
     public Derived1(string x) : base(x) {
        DerivedFields(); //initialize specific fields using ParsedData
     }
}

2 Or you can pass pre parsed string into constructor:

abstract class Base {
     protected ParsedData ParsedData;
     // ...
     public static ParsedData Parse(string x)
     {
        //Parse x here...
     }

     protected Base(ParsedData data) {
        CommonFields(data); //initialize common fields using data
     }
}

class Derived1 : Base {
     public Derived1(ParsedData data) : base(data) {
        DerivedFields(data); //initialize specific fields using data
     }
}

3 Or parse in place of usage:

abstract class Base {
     // ...
     protected Base(string x) {
        var data = Parse(x);
        CommonFields(data); //initialize common fields using data
     }
}

class Derived1 : Base {
     public Derived1(string x) : base(x) {
        var data = Parse(x);
        DerivedFields(data); //initialize specific fields using data
     }
}
Aleksei
Interesting approach - the parse method is actually currently in the Base Class and the resulting object is not kept - to some degree also to memory restrictions. I am surprised that you dam the parse function as the issue, but it sounds logical.
weismat
Sorry, forget to mention method #1 disadvantage: parsed data will remain until object GCed. Also there is no easy way to clear that field, because at some particular constructor you do not know, whether another one child exists or not.
Aleksei