【问题标题】:Static method call in generic manner以通用方式调用静态方法
【发布时间】:2016-05-23 20:40:03
【问题描述】:

我有多个类,例如:

public class Base { }
public class Base1: Base { public static List<Base1> LoadFromXml(string path) }
public class Base2: Base { public static List<Base2> LoadFromXml(string path) }

那我想要一个这样的方法:

public List<T> PrepareBase<T>() where T: Base { return T.Load("C:\test.xml"); }

这样我就不必为每种类型都创建一个方法。 但我不知道如何完成这个或类似的事情。

问题是我不能让基类知道 LoadFromXml 方法,因为静态继承不是一回事。也不是使用静态方法创建单独的接口。

有没有办法做到这一点,还是我期望过高?

编辑: LoadFromXml 方法的一个例子:

public class Base1 
{
    public int ID { get; set; }
    public string PropertyOnlyInBase1 { get; set; }
    public static List<Base1> LoadFromXml(string path)
    {
        List<Base1> baseList = new List<Base1>();
        XDocument doc = XDocument.Load(path);
        foreach(var node in doc.Descendants("Base1"))
        {
            Base 1 base = new Base1() { ID = node.Attributes["id"] };
            base.PropertyOnlyInBase1 = node.Element("PropertyOnlyInBase1");
            baseList.Add(base);
        }
        return baseList;
    }
}

所以基类也有一些独特的属性。这就是为什么我首先需要继承的东西。

【问题讨论】:

  • 不可能。静态方法绑定到它们的定义类。
  • 如果你想要多态,为什么不把它做成一个实例方法呢?还是一个可以返回不同类型列表的通用静态方法?
  • 你打算怎么给PrepareBase打电话?你想用它的输出做什么? (换句话说......假设你可以这样做 - 就像你写的那样。使用它的语句是什么样的?)
  • @Amit:在我的 MainForm.cs 加载方法中:this.base1List = PrepareBase&lt;Base1&gt;(); 并将该列表用作 ComboBox 的数据源。 | @DStanley 在我看来,与实例没有任何联系的方法应该是实例方法,这似乎不合逻辑。
  • @ManuelHoffmann 我同意,但如果你想要多态性,它可能是必要的。

标签: c# inheritance static-methods


【解决方案1】:

一种选择是添加GenericBase

public abstract class Base
{
}
public static class GenericBase<T>
    where T : Base
{
    public static List<T> LoadFromXml(string path)
    {
        //Load from XML
    }
}
public class Base1 : Base { }
public class Base2 : Base { }

public class Test //MainForm.cs class or whatever you want
{
    public void Tester() //Load event handler or whatever you want
    {
        List<Base1> base1List = PrepareBase<Base1>();
    }
    public List<T> PrepareBase<T>() where T : Base
    { return GenericBase<T>.LoadFromXml("C:\test.xml"); }
}

编辑:

正如D Stanley 提到的,这是不可能的;但我做了一些可能对您有所帮助的解决方法:

public abstract class Base
{
    public static List<T> LoadFromXml<T>(string path) where T : Base, new()
    {
        List<T> baseList = new List<T>();
        XDocument doc = XDocument.Load(path);
        foreach (var node in doc.Descendants(typeof(T).Name))
        {
            T t = new T();
            Dictionary<string, string> d = new Dictionary<string, string>();
            foreach (var item in node.Elements())
                d.Add(item.Name.ToString(), item.Value);
            t.Load(d);
            baseList.Add(t);
        }
        return baseList;
    }

    protected internal abstract void Load(Dictionary<string, string> elements);
}
public class Base1 : Base
{
    public string CustomProp1 { get; set; }
    public string CustomProp2 { get; set; }
    public string CustomProp3 { get; set; }

    protected internal override void Load(Dictionary<string, string> elements)
    {
        if (elements.ContainsKey("CustomProp1"))
            CustomProp1 = elements["CustomProp1"];
        if (elements.ContainsKey("CustomProp2"))
            CustomProp2 = elements["CustomProp2"];
        if (elements.ContainsKey("CustomProp3"))
            CustomProp3 = elements["CustomProp3"];
    }
}
public class Base2 : Base
{
    public string CustomProp1 { get; set; }
    public string CustomProp2 { get; set; }
    public string CustomProp3 { get; set; }
    protected internal override void Load(Dictionary<string, string> elements)
    {
        if (elements.ContainsKey("CustomProp1"))
            CustomProp1 = elements["CustomProp1"];
        if (elements.ContainsKey("CustomProp2"))
            CustomProp2 = elements["CustomProp2"];
        if (elements.ContainsKey("CustomProp3"))
            CustomProp3 = elements["CustomProp3"];
    }
}

public class Test //MainForm.cs class or whatever you want
{
    public void Tester() //Load event handler or whatever you want
    {
        List<Base1> base1List = PrepareBase<Base1>();
    }
    public List<T> PrepareBase<T>() where T : Base, new()
    {
        return Base.LoadFromXml<T>("C:\test.xml");
    }
}

【讨论】:

  • 我意识到我遗漏了一个重要细节,因此我在我的问题中添加了有关不同基类中自定义属性的更多信息。
【解决方案2】:

我认为你是正确的,从 XML 加载这些的类应该与 正在加载的类分开。正如你所说,它与实例没有真正的联系。

也许您需要一个单独的类来为您加载这些实例。

public class BaseXmlLoader<TBase> where TBase : Base
{
    public List<TBase> LoadFromXml(string filePath)
    {
        var serializer = new XmlSerializer(typeof(TBase));
        // Load your file and deserialize.
    }
}

好处并不大,因为它没有为您节省那么多代码。但是如果 LoadFromXml 方法除了类型之外基本相同,那么你就会从中得到一些东西。

【讨论】:

  • 它们并不完全相同。 Base1Base2 有一个或两个自定义属性,它们也在 LoadFromXml 方法中加载。这就是为什么我首先尝试使用继承方式。
【解决方案3】:

我改变了解决问题的方法并使用工厂模式解决了它。我还为每个类提供了一个实例方法 SetPropertiesFromXml 来处理自定义属性。与以前使用的方法不同,这样的方法作为实例方法是有意义的。

工厂:

public static class BaseFactory 
{
    public static Base GetBase(string id) 
    { 
        switch(id) { case '1': return new Base1(); ... }
    }

    public static T GetBaseList<T>(string xml, string tagName) where T: Base
    {
        List<T> list = new List<T>();
        var nodes = XDocument.Load(xml).Descendants(tagName);
        foreach(XElement node in nodes)
        {
            var base = GetBase(node.Attribute("id").Value);
            base.SetPropertiesFromXml(node);
            list.Add(base as T);
        }
    }
}

基础

public abstract class Base
{
    public virtual void SetPropertiesFromXml(XElement node) 
    { 
        //<Set Properties like: this.Property = node.Element("key");>
    }
}

public class Base1
{
    public override void SetPropertiesFromXml(XElement node)
    {
        //<Set Custom Properties for Base1>

        //Call Base to add the normal properties as well
        base.SetPropertiesFromXml(node);
    }
}

致电

List<Base1> list = BaseFactory.GetBaseList<Base1>("test.xml", "Base1");

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-04
    • 1970-01-01
    • 1970-01-01
    • 2018-11-25
    • 2012-07-12
    • 1970-01-01
    • 2013-03-20
    相关资源
    最近更新 更多