嵌套列表<>抽象类

时间:2015-08-05 21:26:59

标签: c# list generics abstract-class

感谢您的回复,非常好。然而,在更深入地阅读HashPsi初始评论时,我发现有几个用户都希望将此应用程序与几个不同但非常相似的报告一起使用,例如,有更多的列可用,可以在任何报告中包含/删除给用户。

每个用户只需查看其数据并选择最初导入数据的过滤器,而不是为每种用户类型制定解决方案。 为了在下面的行编辑中保持非常简单,我设置了一个非常简单的id和20x字符串,然后列出预解析的货币/数字,可用于选择下拉数字过滤器或值之间的范围。类似的List用于表单级别的字符串下拉。

完成的应用程序将使用由工作表标识(或类似)分隔的一个数据库。 你有什么想法,我应该使用更多的泛型,如下面的例子(这是非常好的,如果有点高级,因为我仍然是一个可怜的小伙子:))。

public abstract class Entity
{
    public virtual int Id { get; set; }//todo - autoincrement
}

class Sheet
{
    public List<Row> Rows { get; set; }
    public List<ParsedToStringList> ParsedStringLists { get; set; }
}

public class Row : Entity
{
    public int RowId { get; set; } //unique key
    public string String1 { get; set; }
    public string String2 { get; set; }
    public string String3 { get; set; }
    public string String4 { get; set; }
    public string String5 { get; set; }
    public string String6 { get; set; }
    public string String7 { get; set; }
    public string String8 { get; set; }
    public string String9 { get; set; }
    public string String10 { get; set; }

    public string String11 { get; set; }
    public string String12 { get; set; }
    public string String13 { get; set; }
    public string String14 { get; set; }
    public string String15 { get; set; }
    public string String16 { get; set; }
    public string String17 { get; set; }
    public string String18 { get; set; }
    public string String19 { get; set; }
    public string String20 { get; set; }
    public List<ParsedToNumber> ParsedNumbers { get; set; }
}

public class ParsedToNumber
{
    public int ColumnPosition { get; set; }
    public decimal Number { get; set; }
    public int RowId { get; set; } //Foreign key
}

public class ParsedToStringList
{
    public int ColumnPosition { get; set; }
    public List<Filter> FilteredStrings { get; set; }
    public int RowId { get; set; } //Foreign key
}

public class Filter
{
    public string FilterString { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var sheet=new Sheet();
        sheet.Rows.Add(new Row(){
            Id = 1, RowId = 303, String1 = "ABC123", String2 = "Abc Ltd", String3 = "£550.00",
            String4 = "£150.00", String5 = "Wholesale", ParsedNumbers = new List<ParsedToNumber>(){
                new ParsedToNumber() {ColumnPosition = 3, Number = 550.00m, RowId = 303},
                new ParsedToNumber() {ColumnPosition = 4, Number = 150.00m, RowId = 303}
            } });
        sheet.Rows.Add(new Row(){
            Id = 2, RowId = 607, String1 = "XYZ808", String2 = "XYZ Unlimited", String3 = "£999.99",
            String4 = "£55.55", String5 = "Wholesale", ParsedNumbers = new List<ParsedToNumber>() {
                new ParsedToNumber() {ColumnPosition = 3, Number = 999.99m, RowId = 607},
                new ParsedToNumber() {ColumnPosition = 4, Number = 55.55m, RowId = 607}
            } });

        //This provides for persistance of the data in string format
        //and conversion of columns which can be parsed are created 
        //ready for future in dropdown or range selections .

    }
}

我真的玩弄了这个并需要新鲜的眼睛。我假设我可以将类Row中的List Cell更改为可以使用从抽象类Cell继承的具体对象进行转换。

代码的目标是创建一个动态的单元格行,如果可能,可以将其解析为整数,货币,字符串(和其他)类型,并由用户使用。数据从会计数据源的csv文件导入。 我最初创建了一组整数,货币,字符串字段,这些字段效率低但更简单(如果这种方法更好请说)。

class Sheet
{
    public List<Row> Rows { get; set; }
}

public class Row
{
    public string Dummy { get; set; }
    public List<Cell> Cells { get; set; }//Intellisence at <Cell> advises 
}                                        //incorrect number of type parameters?

public class Column
{
    public string Name { get; set; }
}

public enum ColumnType
{
    String,
    Number,
    Currency
}

public abstract class Cell<T>
{
    public Column Column { get; set; }
    public T Data { get; set; }

    public abstract void Parse( object obj );
}

public class StringCell : Cell<string>
{
    public override void Parse( object obj )
    {
        throw new NotImplementedException();
    }
}

3 个答案:

答案 0 :(得分:1)

您可以执行此操作,但需要您设置值类型:

public class Row
{
    public string Dummy { get; set; }
    public List<Cell<Object>> Cells { get; set; } 
} 

但恕我直言,你总是需要通过施法来检查你单元格中对象的类型。如果您的单元格内容标识其所拥有的内容类型会更好。我认为这是你已经尝试过的。我将从我的单元格内容的基类开始:

public abstract class ContentBase
{
    public ContentType Type;
    public Object Value;
}

请注意,ContentType是您的ColumnType,但已重命名。然后你可以让StringContent这样......(你可以根据需要改变set的实现。)

public class StringContent : ContentBase
{
    private string _value;
    public ContentType Type 
    {
        get { return ContentType.String; } 
        private set;
    }
    public string Value
    {
        get { return _value; }
        set { _value = (string)value; }
    }
}

现在,您可以让Cell不要担心其内容(因此您可能不需要将其作为abstract)。

public abstract class Cell<>
{
    public Column Column { get; set; }
    public ContentBase Content { get; set; }
}

让你的Row更简单:

public class Row
{
    public string Dummy { get; set; }
    public List<Cell> Cells { get; set; }
}

答案 1 :(得分:1)

创建对象模型以表示表格/电子表格时,通常需要记住两个主要注意事项:

  1. 将电子表格解析为对象模型。在该级别,列的预期值类型应该决定单元格的构造方式。

  2. 使用对象模型(操作和转换数据,渲染到UI)。在该级别,客户端代码几乎肯定需要区分列允许的不同类型的值(即,此代码将需要打开单元/列的值类型)。例如,UI中的单元格数据的格式化将需要知道单元格是否包含文本,数字或货币量。因此,完全通用的Cell模型将毫无用处。

  3. 为了便于解析和后续使用模型的实用程序,您可以使用工厂为基于与列关联的值类型创建单元格的单元格。对于单元格的下游使用,您可以使用方法返回单元格为每种可能类型(字符串,整数,十进制)保留的值。

    这样的事情:

    class Sheet {
      public List<Row> Rows { get; set; }
    }
    
    public class Row {
      public string Dummy { get; set; }
      public List<Cell> Cells { get; set; } 
    }                                        
    
    public class Column {
      public string Name { get; set; }
      public ValueType ValueType { get; set; }
    }
    
    public class Cell {
      public Column Column { get; private set; }
      public object Data { get; private set; }
    
      public int GetValueInt() {
        return Column.ValueType == ValueType.Number ? (int)Data : 0;
      }
    
      public string GetValueString() {
        // could also return Data.ToString() is Data is not null
        return Column.ValueType == ValueType.String ? (string)Data : null;
      }
    
      public decimal GetValueCurrenty() {
        return Column.ValueType == ValueType.Currency ? (decimal)Data : 0;
      }
    
      // factory for cells
      public static Cell MakeCell(object data, Column column) {
    
        // fail-early if the data does not match the value type specified by the column
        switch (column.ValueType) {
            case ValueType.String:
            if (!(data is string)) {
              throw new ArgumentException("Invalid data for column containing strings");
            }
            break;
    
            case ValueType.Number:
            if (!(data is int)) {
              throw new ArgumentException("Invalid data for column containing ints");
            }
            break;
    
            case ValueType.Currency:
            if (!(data is decimal)) {
              throw new ArgumentException("Invalid data for column containing decimals");
            }
            break;
       }
    
      var cell = new Cell { Column = column, Data = data };
      return cell;
    }
    

    }

答案 2 :(得分:1)

由于int Datestring不共享object以外的常见类型,因此您无法在此模型中使用泛型。

考虑为所有单元格值类型创建一个公共的interfase IValue并从那里继续:

public enum ColumnType
{
    String,
    Date,
    Currency
}

public interface IValue
{
    void Parse(object obj);
}

public class Sheet
{
    public List<Column> Columns { get; set; }
    public Column this[int column] { get { return Columns[column]; } set { Columns[column]=value; } }
}

public class Column
{
    public string Name { get; set; }
    public ColumnType Type { get; set; }
    public List<Cell> Rows { get; set; }
    public Cell this[int row] { get { return Rows[row]; } set { Rows[row]=value; } }
}

public class Cell
{
    public Column Column { get; set; }
    public IValue Data { get; set; }
}

public class StringValue : IValue
{
    public StringValue(string value) { Value=value; }
    public string Value { get; set; }
    public void Parse(object obj)
    {
        Value=obj.ToString();
    }
}
public class DecimalValue : IValue
{
    public DecimalValue(decimal value) { Value=value; }
    public decimal Value { get; set; }
    public void Parse(object obj)
    {
        if(obj is decimal)
        {
            Value=(decimal)obj;
        }
    }
}
public class DateValue : IValue
{
    public DateValue(DateTime value) { Value=value; }
    public DateTime Value { get; set; }
    public void Parse(object obj)
    {
        if(obj is DateTime)
        {
            Value=(DateTime)obj;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var sheet=new Sheet() { Columns = new List<Column>() };
        sheet.Columns.Add(new Column()
        {
            Type=ColumnType.String,
            Name="Item",
            Rows=new List<Cell>()
        });
        sheet.Columns.Add(new Column()
        {
            Type=ColumnType.Date,
            Name="Date",
            Rows=new List<Cell>()
        });
        sheet.Columns.Add(new Column()
        {
            Type=ColumnType.Currency,
            Name="Amount",
            Rows=new List<Cell>()
        });
        sheet[0].Rows.Add(new Cell() { Column=sheet[0], Data=new StringValue("AAB") });
        sheet[0].Rows.Add(new Cell() { Column=sheet[0], Data=new StringValue("AAC") });
        sheet[0].Rows.Add(new Cell() { Column=sheet[0], Data=new StringValue("ABA") });

        sheet[1].Rows.Add(new Cell() { Column=sheet[1], Data=new DateValue(DateTime.Now) });
        sheet[1].Rows.Add(new Cell() { Column=sheet[1], Data=new DateValue(DateTime.Now) });
        sheet[1].Rows.Add(new Cell() { Column=sheet[1], Data=new DateValue(DateTime.Now) });

        sheet[2].Rows.Add(new Cell() { Column=sheet[2], Data=new DecimalValue(1000m) });
        sheet[2].Rows.Add(new Cell() { Column=sheet[2], Data=new DecimalValue(1200m) });
        sheet[2].Rows.Add(new Cell() { Column=sheet[2], Data=new DecimalValue(870m) });

        sheet[0][1].Data.Parse("CCC");

        var check=(sheet[0][1].Data as StringValue).Value;
        // check == "CCC"
    }
}

为了让您的生活更轻松,我会创建您自己的收藏品,而不是使用List<Cell>List<Column>来实现AddColumn()AddCells()等方法。您必须继承System.Collections.ObjectModel.Collection<>