如何在子类中重载静态的,类似工厂的方法 [英] How to override static, factory-like method in subclasses
问题描述
问题:
来自基类的静态方法不能被重写,所以我无法编译这段代码(如果可以的话,我的设计将被解决):
p> public abstract class ParametersBase
{
public static abstract ParametersBase CreateDefault(); //这不是编译器!
}
public abstract class ParametersXml< T>其中T:ParametersBase
{
public static T LoadOrDefault(string fname)
{
System.Threading.Thread.CurrentThread.CurrentCulture =
System.Globalization.CultureInfo。 InvariantCulture的;
结果;
var serializer = new XmlSerializer(typeof(T));
FileStream fs;
使用(fs = new FileStream(fname,FileMode.Open,FileAccess.Read))
结果=(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);
}
返回结果;
$ / code $ / pre
我试图使用泛型在一组子类中实现简单的XML持久性,这些子类表示沿着这些行的参数组:
- 每个
参数
子类都有一些在子类中有所不同的属性集合;每个类都有一个静态的 LoadOrDefault
方法和一个实例 Save
方法;
- 持久性细节(xml序列化和反序列化)将被封装在一个类中;
- 具有默认值的实例化将放在每个类中。
我甚至不确定这些应该是正确的合作,我的意思是,每一个 ParameterBase
需要被 ParametersXml< ;>
,也许正确的做法是通过继承将基础类中的持久化代码封装在基类中。
<这是一个好方法吗?我是否缺少什么?解决方案只需详细说明评论中的内容即可。
将 static
方法标记为 abstract
给出编译时错误。主要是因为你不能将一般意义上的多态性应用于抽象方法(永远不会有虚拟方法调用)。
这个例子需要的是不要将方法标记为 static
。现在由此产生的问题是人们无法确定可以获得 T
的实例。然而,通过进一步限制 T
来让一个不带参数的构造函数通过将类定义更改为:
public abstract class ParametersXml< T>其中T:ParametersBase,new()
现在在 catch
在 LoadOrDefault
中可以这样说:
result = new T()。CreateDefault();
注意这也是一个更清晰的方法,避免使用反射和脏类型转换。 / p>
编辑:
更进一步
正如@ AlexeiLevenkov - 假设 CreateDefault
应该返回自己类型的实例,并且无参数构造函数设置 T
处于默认状态。甚至可以完全删除对 CreateDefault
的需求,并简单地使用无参数构造函数作为 CreateDefault
,从而改变catch处理程序到:
result = new T();
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?
解决方案 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();
这篇关于如何在子类中重载静态的,类似工厂的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!