Search code examples
c#genericsstaticfactorycreation

How to override static, factory-like method in subclasses


PROBLEM:

Static methods from a base class cannot be overriden, so I cannot compile this code (if I could, my design would be solved):

public abstract class ParametersBase
{
    public static abstract ParametersBase CreateDefault();  // THIS DOES NOT COMPILE!
}

public abstract class ParametersXml<T> where T : ParametersBase
{
    public static T LoadOrDefault(string fname)
    {
        System.Threading.Thread.CurrentThread.CurrentCulture =
            System.Globalization.CultureInfo.InvariantCulture;

        T result;

        var serializer = new XmlSerializer(typeof(T));
        FileStream fs;

        try
        {
            using (fs = new FileStream(fname, FileMode.Open, FileAccess.Read))
                result = (T)serializer.Deserialize(fs);
        }
        catch (InvalidOperationException)
        {
            result = (T)typeof(T).GetMethod("CreateDefault").Invoke(null, new object[] { });
            using (fs = new FileStream(fname, FileMode.Create, FileAccess.Read))
                serializer.Serialize(fs, result);
        }

        return result;
    }
}

CONTEXT:

I am trying to use generics to implement simple XML persistence in a set of subclasses representing groups of parameters along these lines:

  • Each Parameter subclass has some set of properties that vary among subclasses;
  • Each class will have a static LoadOrDefault method, and an instance Save method;
  • Details of persistence (xml serialization and deserialization) will be encapsulated in a single class;
  • Instantiation with default values will be put in each respective class.

I'm not even sure these should be the correct collaborations, I mean, every ParameterBase needs to be "wrapped" by a ParametersXml<>, while perhaps the right thing would be to encapsulate persistence code inside the base class, via inheritance...

Is this a good approach? Am I missing something?


Solution

  • Just to elaborate what went on in the comments.

    Marking a static method as abstract gives a compile time error. Mainly due to the fact that you cannot apply polymorphism in the general sense to abstract methods (there would never be a virtual method call).

    What is needed in this instance is to not mark the method as static. Now the problem that arises from this is that one cannot be sure that one can acquire an instance of T. However, this can be changed by further constraining T to have a constructor taking no arguments, by changing the class definition to:

    public abstract class ParametersXml<T> where T : ParametersBase, new()
    

    Now in the catch in LoadOrDefault it is possible to say:

    result = new T().CreateDefault();
    

    Notice how this is also a lot cleaner, and avoids both use of reflection and dirty type casts.

    EDIT: Going even further

    As pointed out by @AlexeiLevenkov - assuming that CreateDefault is supposed to return an instance of its own type, and that the parameterless constructor is setting up T in its default state. One could even completely remove the need for CreateDefault and simply use the parameterless constructor as CreateDefault, thus changing the line in the catch handler to:

    result = new T();