代码之家  ›  专栏  ›  技术社区  ›  yoozer8

要求接口实现具有静态Parse方法

  •  6
  • yoozer8  · 技术社区  · 12 年前

    我有一个最小的接口,将处理一组对象,这些对象的类实现了这个接口。集合(及其相关功能)不关心这些对象的任何细节,除了它们的名称、将它们转换为XML的能力以及从XML解析它们的能力。

    接口的未来实现将对集合的元素做更多的处理,并且显然将实现它们自己的Parse和ToXml方法(当遇到这些项时,集合将使用它们来适当地解析这些项)。

    不幸的是,我无法在接口中列出静态Parse方法(我已经阅读 these three questions ). 让Parse方法需要一个实例对我来说是没有意义的。有没有什么方法可以要求接口的所有实现都有一个静态Parse方法?

    public interface IFoo
    {
      string Name { get; }
    
      string ToXml();
    
      static IFoo Parse(string xml); // Not allowed - any alternatives?
    }
    
    6 回复  |  直到 7 年前
        1
  •  6
  •   spender    12 年前

    你不能那样做。而且静态方法无论如何都不是多态的,所以它不会有太大的意义。

    你想要的是某种工厂模式。

        2
  •  2
  •   Joe Enos    12 年前

    假设 Parse 获取一个字符串并将其转换为一个完全填充的对象 Hydrate 方法,例如:

    interface IFoo {
        string Name { get; set; }
        int Age { get; set; }
        void Hydrate(string xml);
    }
    
    class Foo : IFoo {
        public string Name { get; set; }
        public int Age { get; set; }
    
        public void Hydrate(string xml) {
            var xmlReader = ...etc...;
            Name = xmlReader.Read(...whatever...);
            ...etc...;
            Age = xmlReader.Read(...whatever...);
        }
    }
    
    void Main() {
        IFoo f = new Foo();
        f.Hydrate(someXml);
    }
    

    或者说流利一点:

    public IFoo Hydrate(string xml) {
        // do the same stuff
        return this;
    }
    
    void Main() {
        IFoo f = new Foo().Hydrate(someXml);
    }
    
        3
  •  1
  •   Alexander Tsvetkov    12 年前

    我想到的唯一选择是使用抽象类,而不是这里的接口。然而,无论如何,您都无法覆盖子类中静态方法的行为。

    您可以使用Factory模式并要求类实现类似的行为 IFoo 要有对该工厂的引用(可以通过构造函数注入在其中):

    public interface IFoo
    {
        string Name { get; }
    
        string ToXml();
    
        IFooFactory FooFactory { get; }
    }
    
    public interface IFooFactory
    {
        IFoo Parse(string xml);
    }
    
        4
  •  1
  •   npclaudiu    12 年前

    我会将所有与序列化相关的方法提取到不同的接口中。请考虑以下示例:

    public interface IFoo
    {
        string Name { get; }
        IFooSerializer GetSerializer(string format);
    }
    
    public enum FooSerializerFormat { Xml, Json };
    
    public interface IFooSerializer
    {
        string Serialize(IFoo foo);
        IFoo Deserialize(string xml);
    }
    
    public class Foo : IFoo
    {
        public string Name { get; }
    
        public IFooSerializer GetSerializer(FooSerializerFormat format)
        {
            case FooSerializerFormat.Xml:
                return new FooXmlSerializer();
    
            case FooSerializerFormat.Json:
                return new FooJsonSerializer();
        }
    }
    
    public class FooXmlSerializer : IFooSerializer { /* Code omitted. */ }
    public class FooJsonSerializer : IFooSerializer { /* Code omitted. */ }
    
        5
  •  0
  •   Beachwalker    12 年前

    也许是这样?

    public interface IFoo
    {
      string Name { get; }
    
      string ToXml();
    
      IFoo Parse(string xml);
    }
    
    public abstract class AFoo : IFoo
    {
      public string Name { get; set; }
    
      public string ToXml() { };
    
      public IFoo Parse(string xml) { return AFoo.StaticParse(xml); };
    
      public static IFoo StaticParse(string xml) { };  // implement one here
    }
    

    即使以上可能是一个解决方案,我也鼓励您使用abstract工厂和/或模板方法。看见 Template Method Pattern 相反另一种选择可能是使用 Extension method 如果您不想在几个实现之间共享它。

        6
  •  0
  •   JerKimball    12 年前

    总的来说,众所周知(有时)我会将Extension方法用于以下内容:

    public interface IFoo
    {
        string Name {get;}
        string ToXml();    
    }
    
    public class Foo : IFoo
    {
        public Foo(string name)
        {
            Name = name;
        }
        public string Name {get; private set;}
        public string ToXml()
        {
            return "<derp/>";
        }
    }
    

    这就是实例,让我们处理“静态”部分:

    public static class FooExts
    {
        public static IFoo Parse(this string xml)
        {
            return new Foo("derp");
        }
    }
    

    还有一个测试:

    void Main()
    {
        var aFoo = "some xml".Parse();
        Console.WriteLine(aFoo.ToXml());
    }
    

    正如@Jim所提到的,在有些情况下你不会 希望 Foo 返回,在这种情况下,您可能会使用以下内容:

    public static T Parse<T>(
        this string xml, 
        Func<string, IFoo> useMeUseMe = null) 
        where T:IFoo
    {
        if(useMeUseMe == null)
            useMeUseMe = (x => new Foo(x));
        return (T)useMeUseMe("derp");
    }
    

    遗憾的是,当我们偏离“规范”时,我们现在必须告诉方法我们想要什么:

    var aFoo = "some xml".Parse<Foo>();
    Console.WriteLine(aFoo.ToXml());    
    var aBar = "some xml".Parse<Bar>(s => new Bar(s));
    Console.WriteLine(aBar.ToXml());