有没有一种方法可以使用单个类来处理具有两个不同结构记录的数组?

Posted

技术标签:

【中文标题】有没有一种方法可以使用单个类来处理具有两个不同结构记录的数组?【英文标题】:Is there a way to use a single class that does work on an array with two differently structured records? 【发布时间】:2019-09-27 21:15:29 【问题描述】:

我希望从输入文件中读取数据,并使用 ListClass 中的类方法将传入的数据存储到结构数组中。我已经做到了。然后,我希望从一个与第一个文件格式不同的单独文件读入一个单独的数组,但使用相同的 ListClass。这可能吗?

我尝试过使用抽象类,但似乎不太正确。举个小例子:

 class ListClass 
            
     public struct struct1
     
         public string strPart;
         public string strDescrip;
         public int intQty;
       

     public struct struct2
     
         public string strPart;
         public char chrSold;
         public int intQtySold;
     

     public int MAX_ELEMENTS = 4;

     //PRIVATE DATA MEMBERS
     private arr[] listArr;
     private int length;
     private int currPos;

     public ListClass()
     
         length = 0;
         currPos = 0;
         listArr = new arr[MAX_ELEMENTS];
     


     public ListClass(int size)
     
         listArr = new Array[size];
     


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    //Purpose:  Create a deep copy of the list
    //Pre:      List has been instantiated and orig contains list to be copied
    //Post:     An identical, deep copy of the list has been created.
    public ListClass(/*in*/ ListClass orig) //list to be copied
    
        length = orig.length;
        //currPos = orig.currPos;

        // Allocate the new list
        listArr = new Array[MAX_ELEMENTS];

        // Copy over all the values
        for (int i = 0; i < MAX_ELEMENTS; i++)
            listArr[i] = orig.listArr[i];
    //end copy constructor


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    //Purpose:  Indicates whether or not the list is empty
    //Pre:      List has been instantiated
    //Post:     Returns true if list is empty and false, otherwise
    public bool IsEmpty()
    
        return (length == 0);
    //end IsEmpty


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    //Purpose:  Indicates whether or not the list is full
    //Pre:      List has been instantiated
    //Post:     Returns true if list is full and false, otherwise
    public bool IsFull()
    
        return false;
     //end IsFull


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    //Purpose:  Inserts item into the list
    //Pre:      List is not full
    //Post:     Item has been inserted at the end of the current list, length has 
    //          been modified 
    //Error Handling:
    //          if the key already exists within list, do not insert newItem and return false 
    //          if the list is full, do not insert the item and return false

    public void Insert(/*in*/ Array newItem)        //item to be added
    
        // Make sure there is space
        if (NeedToExpand())
            //Expand if needed and insert new item
            Expand();

        listArr[length] = newItem;
        length++;
     //end Insert


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    //Purpose:  Deletes an item from the list
    //Pre:      Method Find has been called to find the item to delete, and the 
    //          that item is in the list.CurrPos now points at the item to be deleted
    //Post:     The item denoted by currPos has been deleted from the list, lemgth has
    //          been updated.
    //Error Handling: If the list is empty, no changes are made
    public void Delete(string key)
    
        if (IsEmpty())
            Console.WriteLine("List is Empty");
        else
        
            if(!Find(key))
                Console.WriteLine("Item not Found");
            else
            
                if (length > 0)
                
                    for (int i = currPos; i < length; i++)
                        listArr[i] = listArr[i + 1];

                    length--;
                

                if (NeedToShrink())
                    Contract();
            
        
    //end Delete


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    //Purpose:  Moves to the beginning of the list
    //Pre:      List has been instantiated
    //Post:     currPos has been set to the first position in the list
    public void FirstPosition()
    
        currPos = 0;
    //end FirstPosition


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    //Purpose:  Moves to the next element in the list
    //Pre:      List has been instantiated
    //Post:     currPos has been moved to the next position in the list
    //Error Handling: if currPos is already at the end of the list, currPos is not modified
    public void NextPosition()
    
        if (!EndOfList())
            currPos++;
        else
            Console.WriteLine("End of List");
     //end NextPosition


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    //Purpose:  Determines if currPos is at the end of the list
    //Pre:      List has been instantiated
    //Post:     Returns true if currPos is at the end of the list, and false, otherwise
    //          end-of-list denotes the first empty index in the list.

    public bool EndOfList()
    
        return (currPos == length - 1);
    //end EndOfList


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~        


    //Purpose:  Determines whether or not item is in the list
    //Pre:      item is assigned a value
    //Post:     If item is in the list then true is returned  and currPos contains
    //                the index of the item in the list, otherwise, 
    //                false is returned and currPos is at zero.
    public bool Find(/*in*/ string key)     // item to be found
    
        bool found = false;
        currPos = 0;

        while (!found && currPos < length)
        
            if (listArr[currPos] == key)
                found = true;
            else
                currPos++;
        // End while

        if (!found)
            currPos = 0;

        return found;
    //end Find


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    //Purpose:  Returns the current item in the list(denoted by currPos)
    //Pre:      List is not Empty
    //Post:     Returns the item at currPos
    //Error Handling: if Retrieve is called on an empty list an InvRec with a key set 
    //                  to int.MinValue is returned. 
    public object Retrieve()
    
        object inv = new object();

        if (!IsEmpty())
            return listArr[currPos];
        else
        
            Console.WriteLine("The list is empty");
            return inv;
        

    //end Retrieve


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    //Purpose: Clears the list
    //Pre: List has been instantiated
    //Post: List has been restored to its initial condition
    public void Clear()
    
        currPos = 0;
        length = 0;
    //end Clear

    //Purpose: indicates if the list needs to be expanded
    //Pre: insert must be called with a new item to insert
    //Post: returns true if length equals MAX_ELEMENTS, false otherwise
    private bool NeedToExpand()
    
        if (length == MAX_ELEMENTS)
            return true;
        else
            return false;
    

    //Purpose: indicates if the list needs to be shrunk
    //Pre: delete must be called with a item to delete
    //Post: returns true if length is 25% of MAX_ELEMENTS, false otherwise
    private bool NeedToShrink()
    
        if ((float)MAX_ELEMENTS * 0.25f == length)
            return true;
        else
            return false;

    

    //Purpose: to expand the space of the list
    //Pre: NeedToExpand must return true
    //Post: the size of the list is doubled
    private void Expand()
    
        MAX_ELEMENTS *= 2;

        ListClass tempList = new ListClass(MAX_ELEMENTS);

        for (int i = 0; i < length; i++)
            tempList.listArr[i] = listArr[i];

        listArr = tempList.listArr;
    

    //Purpose: to contract the size of the list
    //Pre: NeedToContract must return true
    //Post: the size of the list is shrunk
    private void Contract()
    
        if(MAX_ELEMENTS != 4)
        
            MAX_ELEMENTS /= 2;

            ListClass tempList = new ListClass(MAX_ELEMENTS);

            for (int i = 0; i < length; i++)
                tempList.listArr[i] = listArr[i];

            listArr = tempList.listArr;
                    
    

如果代码 sn-p 有意义。我有两个不同格式的结构。我想使用 ListClass 中的所有方法存储到数组中。

【问题讨论】:

使用 ListClass 中的类方法 除了构造函数之外,这里没有任何方法 我可以全部添加。 非常不清楚你想做什么以及你有什么问题......帖子中的代码似乎是List&lt;string&gt;的某种重新实现......但你问的是把一些结构...只是疯狂的猜测-您是在问如何使您的类通用类似于常规List&lt;T&gt;? (以ListClass&lt;T&gt; where T:struct...开头) 是的,我真的不知道如何用语言表达。我有两个具有不同数据结构的输入文件。我可以单独使用 ListClass 读取每个文件。我想要做的是拥有一个 ListClass 实例并将其用于两个输入文件。我唯一的问题是结构具有不同的结构。所以我想不出一种方法让 if 对这两个结构都有效。希望这是一个更好的解释。 我最大的问题是我的私人名单Arr。我不知道它需要什么数据类型。 【参考方案1】:

好吧,好吧……鉴于这篇文章是用 C# 标记的,我建议使用 C# 的常规方法。

第一条评论,C# 内置了很多集合,因此通常不会尝试重新编写它们。还有一点,Struct1 和 Struct2 的类型不同,看起来它们的含义也不同——我们可以将它们重命名为 InventoryAmount 和 SoldAmount。

所以你只需要在你的主类中声明两个列表,像这样

using System.Collections.Generic;
...
List<InventoryAmount> InventoryAmounts  get;  = new List<InventoryAmount>();
List<SoldAmount> SoldAmounts  get;  = new List<SoldAmount>();

然后您读取数组并将它们添加到列表中,如下所示:

InventoryAmount[] inventoryAmounts = ...
this.InventoryAmounts.AddRange( inventoryAmounts); 

SoldAmount[] soldAmounts = …
this.SoldAmounts.AddRange( soldAmounts);

实际上,这种方法会分配一些额外的内存,因为数组在添加到列表后不会被使用。您可以通过在读取每个值时将其直接放入列表中来避免这种情况。或者您可以将它们留在数组中。

另一条评论:FirstPosition、NextPosition 等方法通常使用 Enumerator 和 Enumerable 完成。这些内置在 List 类和数组中,并由“foreach”使用,如下所示:

foreach (InventoryAmount ia in this.InventoryAmounts) 
   ...


foreach (InventoryAmount ia in inventoryAmounts) 
   ...

【讨论】:

我明白你在说什么。但这并不是我的问题所在。我的问题是我不能将 ListClass 与两个结构一起使用,因为我的 listArr 不包含可以同时包含两个结构的正确格式。如果这有意义【参考方案2】:

回应您的评论,我真的不建议继续使用 ListClass 代码。最好利用 c# 中内置的列表。

另一方面,如果你真的决心以艰难的方式去做(即可能是错误的方式),那么我是谁来阻止你?

两种主要的困难方法是 (a) 声明一个可以包含 InventoryAmount 或 SoldAmount 的结构,或者 (b) 将数组声明为 object[] 并使用 c# 装箱。从业务角度来看,这两种方法都可能毫无意义,而且效率低下。

对于第一种方法(a),只需声明一个联合结构,例如:

public struct InventoryAmountOrSoldAmount 

    public InventoryAmountOrSoldAmount( InventoryAmount ia => this.InventoryAmount = ia;
    public InventoryAmountOrSoldAmount( SoldAmount sa => this.SoldAmount = sa;
   public InventoryAmount  get;
   public SoldAmount  get;

然后将其用作数组的类型:

private InventoryAmountOrSoldAmount[] listArr = new InventoryAmountOrSoldAmount[MAXELEMENTS];

或者第二种方法(b)是这样声明数组:

private object[] listArr = new object[MAXELEMENTS];

然后,您可以为数组的每个元素分配 InventoryAmount 或 SoldAmount。 C# 将通过在堆上放置一个副本并将指向它的指针放在数组元素中来装箱结构。

【讨论】:

以上是关于有没有一种方法可以使用单个类来处理具有两个不同结构记录的数组?的主要内容,如果未能解决你的问题,请参考以下文章

我想在单个表格视图中使用来自 nib 的两个不同的自定义单元格。两个原型电池具有不同的高度

从单个csv文件中读取两个完整的不同数据帧

两个 Java 类的比较

组合模式

组合模式

对具有不同大小的多个数组使用一种方法[重复]