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:
Parameter
subclass has some set of properties that vary among subclasses;LoadOrDefault
method, and an instance Save
method;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?
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();