2011-08-04 84 views
3

我正在编写一个允许用户运行测试的应用程序。测试由许多不同的对象组成,例如配置,温度和基准。设置等在xml之间来回保存。我在我的代码中传递了不同的XElements,所以我可以针对不同情况构建最终的xml文档。我愿做这样的事情:C中的静态方法模板#

public abstract class BaseClass<T> 
{ 
    abstract static XElement Save(List<T>); 
    abstract static List<T> Load(XElement structure); 
} 

public class Configuration : BaseClass<Configuration> 
{ 
    public string Property1 { get; set; } 
    public string Property2 { get; set; } 
    //etc... 

    public static XElement Save(List<Configuration>) 
    { 
     XElement xRoot = new XElement("Root"); 
     //etc... 
     return xRoot; 
    } 

    public static List<Configuration> Load(XElement structure) 
    { 
     List<BaseClass> list = new List<BaseClass>(); 
     //etc... 
     return list; 
    } 
} 

public class Temperature : BaseClass<Temperature> 
{ 
    public float Value { get; set; } 

    public static XElement Save(List<Temperature>) 
    { 
     //save 
    } 

    public static List<Temperature> Load(XElement structure) 
    { 
     //load 
    } 
} 

[编辑]:修订问题(以上功能更改签名)[/编辑]

当然,我没有真正允许占优的静态方法BaseClass的。解决这个问题的最好方法是什么?我想尽可能多的以下的为有效地:

List<Temperature> mTemps = Temperature.Load(element); 
List<Configuration> mConfigs = Configuration.Load(element); 

Temperature.Save(mTemps); 
Configuration.Save(mConfigs); 

[编辑] [/编辑]

我能想到的唯一的解决方案上述更改预期使用代码在下文中,这是不可接受:

public class File 
{ 
    public static XElement Save(List<Temperature> temps) 
    { 
     //save temp.Value 
    } 

    public static XElement Save(List<Configuration> configs) 
    { 
     //save config.Property1 
     //save config.Property2 
    } 

    //etc... 
} 
+1

为什么这些方法必须是静态的? – BoltClock

+0

我希望能够保存配置列表,而无需创建配置实例。虽然这是可能的,但似乎并不需要。 – AGuyInAPlace

回答

3

静态方法不是类实例的一部分。因此,压倒他们并没有任何意义。他们无法访问他们恰好是其成员的实例的非静态部分。

这是一种策略模式场景,例如,你可以只有一个静态加载&保存方法,检查传递给它们的对象的类型,并相应地采取行动。但是这里有另一种更聪明的方式,它使用泛型类型来创建原型并调用它的方法,从而允许您将逻辑保留在每个派生对象类型中。

(编辑再次)

下面是它的另一个裂缝,沿着相同的路线是我原来的建议。实际上,我测试了它并且它可以正常工作,所以我认为这是你可以做的最好的事情来获得你正在寻找的所有功能(除了有条件地测试类型和调用代码之外)。您仍然需要传递Load的类型,否则,运行时将不知道预期的返回类型。但是Save工作普遍。而子类实现是强类型的。

这只是使用列表中的第一个对象作为其原型,很简单。

public interface IBaseObject 
{ 
    XmlElement Save(IEnumerable<IBaseObject> list); 
    IEnumerable<IBaseObject> Load(XmlElement element); 
} 
public interface IBaseObject<T> where T: IBaseObject 
{ 
    XmlElement Save(IEnumerable<T> list); 
    IEnumerable<T> Load(XmlElement element); 
} 

public class Temperature : IBaseObject<Temperature>, IBaseObject 
{ 

    public XmlElement Save(IEnumerable<Temperature> list) 
    { 
     throw new NotImplementedException("Save in Temperature was called"); 
    } 

    public IEnumerable<Temperature> Load(XmlElement element) 
    { 
     throw new NotImplementedException("Load in Temperature was called"); 
    } 

    // must implement the nongeneric interface explicitly as well 

    XmlElement IBaseObject.Save(IEnumerable<IBaseObject> list) 
    { 
     return Save((IEnumerable<Temperature>)list); 
    } 

    IEnumerable<IBaseObject> IBaseObject.Load(XmlElement element) 
    { 
     return Load(element); 
    } 
} 

// or whatever class you want your static methods living in 

public class BaseObjectFile 
{ 
    public static XmlElement Save(IEnumerable<IBaseObject> list) 
    { 
     IBaseObject obj = list.DefaultIfEmpty(null).First(); // linq 
     return obj==null ? null : obj.Save(list); 
    } 
    public static IEnumerable<IBaseObject> Load<T>(XmlElement element) 
     where T: IBaseObject, new() 
    { 
     IBaseObject proto = new T(); 
     return proto.Load(element); 
    } 
} 

(原编辑)

这有一个问题,你必须调用与类型,例如静态方法

BaseClass<Temperature>.Load() 

对于Save方法有一个解决方法,但是你想要的部分是不可能的。Load方法无法知道要返回哪种类型的列表,因为它的唯一参数没有关于返回类型的信息。因此,它不可能决定将哪种类型创建为原型。所以无论如何,如果你想使用普通的Load方法,你必须把它传递给一个像上面语法那样的类型。

对于Save方法,您可以使用反射在静态方法中创建原型,方法是从第一个元素获取类型,然后从原型调用Save方法。所以如果你只需要使用Save方法就可以了。

不过说到底,我认为这将是很多简单的做这样的事情:

public static XElement Save(List<IBaseClass> list) 
{  
    if (list is Temperature) { 
     // do temperature code 
    } else if (list is SomethingElse) { 
     // do something else 
    } 
} 

反正 - 就像我说这将需要反思,使即使是保存方法工作以这种方式。我只是使用简单的方法。

(原始坏代码移除)

+0

此外,不相关,但是对于以下划线'_'开头的方法名称是否有任何意义?即使在其他语言,如C++,Java等?这是我不时看到的东西,我很好奇为什么这样做。 – AGuyInAPlace

+0

这样,你不能''BaseClass.Save(data)',你必须指定'data'的具体类型,这正是OP试图避免的。你可以通过使用类型推断来解决这个问题,但这需要列表是例如'列表',似乎并非如此。 – svick

+0

这是一种常用于内部使用方法的惯例。在这里我们遇到了一个问题 - 你想要的一部分不是真的可能,我即将更新答案。 –

1

如果您不太关心保存的格式,可以自由使用序列化(内部使用反射)。

string SerialiseToString<T>(T source) 
{ 
    using (StringWriter sw = new StringWriter() && XmlSerializer xml = new XmlSerializer(typeof(OrderedItem))) 
    { 
     xml.Serializer(sw, source); 
     return sw.ToString(); 
    } 
} 

如果你想将其纳入您的XML文件的较大部分,最简单的方法是分析此输出,并把它添加到你的。或者,您可以自己反映这些属性。

+0

恐怕目的是为save()和load()函数有一个独特的实现,这取决于我目前使用的BaseClass类型。每种类型都有独特的附加信息,必须从xml发送/解析。 – AGuyInAPlace

1

如果共享部分是相同的,也可以把它在BaseClass

public static XElement Save(IEnumerable<BaseClass> list) 
{ 
    var root = new XElement("root"); 
    foreach (var item in list) 
    { 
     item.Save(root); 
    } 
    return root; 
} 

这里,Save(XElement)是一个虚拟方法,每种类型的实现它。

很明显,你不能在加载时做到这一点,你必须知道你正在加载什么类型,或者有一些方法来找出你正在加载哪种类型。

+0

问题是关于处理对象列表的方法。我的假设是,不同的实现需要对列表做更多的事情,而不仅仅是保存每个元素。否则,这个问题是微不足道的,而且首先要有一个像这样的静态方法是没有用的。 –

+0

正确。从整个列表中收集信息,例如最高温度和最低温度,或基准列表的预计运行时间。这些信息是特定于案例的,这意味着每个派生类都需要一个唯一的save()和load() – AGuyInAPlace