开发者

A Template for Static Methods in C#

开发者 https://www.devze.com 2023-03-25 04:04 出处:网络
I am writing an application that allows a user to run a test. A test consists of a number of different objects, such as configuration, temperature, and benchmark. Settings and the like are saved back

I am writing an application that allows a user to run a test. A test consists of a number of different objects, such as configuration, temperature, and benchmark. Settings and the like are saved back and forth between xml. I pass different XElements around in my code so I can build the final xml document differently for different situations. I wish to do something like this:

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
    }
}

[EDIT]: Revising question (Changed signatures of above functions)[/EDIT]

Of course, I am开发者_开发技巧 not actually allowed to override the static methods of BaseClass. What is the best way to approach this? I would like as much of the following to be valid as possible:

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

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

[EDIT]Changed intended usage code above[/EDIT]

The only solution I can think of is the following, which is NOT acceptable:

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...
}


Static methods aren't part of a class instance. So overriding them doesn't make any sense anyway. They can't access any nonstatic part of an instance that they happen to be a member of.

This is kind of a strategy pattern scenario, e.g. you could just have single static Load & Save methods that check the type of object passed to them, and act accordingly. But here's another slightly more clever way that uses generic types to create a prototype and call its method, allowing you to keep the logic within each derived object type.

(edit again)

Here's another crack at it, along the same lines as my original suggestion. I actually tested this and it works, so I think this is the best you can do to get all the functionality you are looking for (other than testing types and calling code conditionally). You still need to pass a type for Load, otherwise, the runtime would have no idea what kind of return is expected. But Save works universally. And the subclass implementations are strongly typed.

This just uses the first object in the list as its prototype, simple enough.

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);
    }
}

(original edit)

This has a problem in that you must call the static methods with a type, e.g.

BaseClass<Temperature>.Load()

There is a way around this for the Save method, but part of what you want is not possible. The Load method cannot know what type of list to return because its only parameter has no information about the return type. Hence, it can't possibly decide which type to create as a prototype. So no matter what, if you wanted to use common Load method, you would have to pass it a type like the above syntax.

For the Save method, you could use reflection to create the prototype in the static method, by obtaining the type from the first element, and then call the Save method from the prototype. So if you only need the Save method to be used as you like, that much is possible.

Ultimately, though, I think it would be a lot simpler to do something like this:

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

Anyway - like I said it's going to require reflection to make even the Save method work in this way. I'd just use the simple approach.

(original bad code removed)


If you don't really care about the format in which its saved, you're free to use serialisation (which uses reflection internally).

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

If you want to incorporate it into a larger part of your XML file, the easiest way would be to parse this output and add it to yours. Alternatively, you could reflect the properties yourself.


If the shared part is the same, you can put it in BaseClass:

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

Here, Save(XElement) is a virtual method, each type implements it.

Obviously, you can't do this with loading, you either have to know what type are you loading, or have some way of finding out which type are you loading.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号