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

如何最好地在C#中构建类继承结构以允许访问父类成员?

c#
  •  1
  • Alex  · 技术社区  · 16 年前

    在决定如何最好地在C#(.NET 3.5)中描述特定的数据结构时,我遇到了几个问题。

    我必须创建一个类库,为命令行程序的输入创建一些文本文件。有几十种文件类型,但它们中的大多数都有类似的页眉、页脚和其他一些属性。

    因此,一个文件可能看起来像这样:

    timestamp filename company
    
    ....lots of attributes that make up the file body....
    
    footerdetail1 footerdetail2 footerdetail3
    

    现在,在这几十种文件类型中,实际上只有三种独特的页眉/页脚组合。

    因此,我想创建一个包含大部分headerItem和footeritems的父类,然后在派生类中实现实际的文件类型:

    class BaseFileType {
    
    public List<HeaderItems>; 
    public List<BodyItems>;   
    public List<FooterItems>;
    FileType filetype;
    String filename;
    
    public BaseFileType1 (FileType filetype,String filename) {
    
       this.filetype = filetype;
       this.filename = filename;
    
    
       // add stuff to the header list
       // add stuff to the footer list
    
    
    } 
    
        // some methods to find items in the header/ footer lists
    
    }
    
    class ActualFile1 : BaseFileType {
    
        ActualFile() {
    
    
        //add stuff to the bodyitems list
        }
    
    
        //some other generic methods
    
    
    } 
    

    问题是,我想从基类继承派生类的构造器,但根据我所读到的,这是不可能的。这里最好的策略是什么?

    我已经看到,我可以从我的派生类构造函数中调用基本构造函数,如下所示:

    ActualFile():基(父参数)

    这是完成任务的最佳方式吗?最后,我只是在为所有这些文件信息寻找最佳的数据结构,这样我在创建这些类时就不需要重复了。

    除了人际关系,还有什么其他选择可行?有一个FileFactory可以吐出包含我所需结构的类吗?

    3 回复  |  直到 16 年前
        1
  •  0
  •   Gishu    16 年前

    这看起来像是模板方法可以解决的问题。..(最后是一个使用模式的地方:)

    public abstract class DocumentGenerator
    {
      abstract protected void AddHeader();
      abstract protected void AddBody();
      abstract protected void AddFooter();
    
      // Template Method http://en.wikipedia.org/wiki/Template_method_pattern
      public FileInfo Generate(string sFileName)
      {
        FileInfo f = null;  
        //create file
    
        // either pass in the file to the below methods or use member vars + an additional write step 
        AddHeader();
        AddBody();
        AddFooter();
    
        // write out everything to the file
        return f;
      }
    }
    

    接下来,对于3个独特的组合中的每一个,中间级别的类

    abstract public class UniqueHeaderFooterCombination1 : DocumentGenerator
    {
      protected override void AddHeader()
      {
        Console.WriteLine("unique combination1 header");
      }
      protected override void AddFooter()
      {
        Console.WriteLine("unique combination1 footer");
      }
    }
    

    最后,每个唯一文件类型的具体类

    public class LetsSayCSVGenerator : UniqueHeaderFooterCombination1
    {
      protected override void AddBody()
      {
        Console.WriteLine("csv body items");
      }
    }
    

    将其与Factory结合,为FileType返回正确类型的生成器,您就完成了。

    abstract class DocumentGeneratorFactory
    {
      public static DocumentGenerator GetGenerator(FileType eFileType)
      {
        switch (eFileType)
        {
          // a hash of FileType => Generator
          default:
            return new LetsSayCSVGenerator();
        }
    
      }
    }
    
    class Program
    {
      static void Main(string[] args)
      {
        DocumentGenerator d = DocumentGeneratorFactory.GetGenerator(FileType.CSV);
        FileInfo f = d.Generate("ItsAlive.CSV");
      }
    }
    
        2
  •  0
  •   Marc Gravell    16 年前

    正如你所说,构造函数不是继承的。

    如果您需要在构造过程中知道值,那么强制派生类在构造函数中提供详细信息将是理想的情况。如果没有,请考虑派生类必须提供的抽象属性:

    // base-class
    public abstract FileType FileType {get;}
    
    // derived-class
    public override FileType FileType {
        get {return FileType.Excel;} // or whatever
    }
    

    请注意,除非你记录下来 非常 显然(并理解其含义),你不应该在构造函数内调用虚拟方法(如上所述)(因为派生类型的构造函数尚未执行),但之后就可以了。

    由于文件名并不真正依赖于子类型,我怀疑无论如何我都会把它作为一个ctor参数。

        3
  •  0
  •   dmo    16 年前

    你可以创建一个基类来读取headers&;页脚,并提供对在中间属性的访问。然后定义一个接口,几十个特定的属性读取器将实现该接口。接口可能需要接受一个“基类”,每个实现都将定义如何读取中间的内容。

    在实践中,您将有一种读取公共部分的通用方法和访问属性的通用方法。这些部分是松散耦合的,因此更改的限制性较小。