【问题标题】:Failing to sort an Array containing objects. C#无法对包含对象的数组进行排序。 C#
【发布时间】:2016-06-09 10:26:50
【问题描述】:

我正在做一个学校项目,我花了 5 个小时试图了解如何使用包含四个维度的对象对数组进行排序。我打算按productCodedrinkName 对它们进行排序。当我阅读各种线程时,人们告诉 OP 使用 LINQ。我不应该使用它,而且我越来越困惑使用什么方法。老师告诉我,要使用冒泡排序(我知道算法不好),我在包含一维的数组上做得很好。我尝试了Array.Sort,但后来我得到了System.InvalidOperationException

我要疯了,即使我阅读了有关该主题的多个主题,我也被困住了。我可能以错误的方式使用ToString。任何轻推将不胜感激。

class soda
{
    string drinkName;   
    string drinkType;
    int drinkPrice;
    int productCode;

    //Construct for the beverage
    public soda(string _drinkName, string _drinkType, int _drinkPrice, int _productCode)
    {
        drinkName = _drinkName;
        drinkType = _drinkType;
        drinkPrice = _drinkPrice;
        productCode = _productCode;
    }

    //Property for the drink name e.g. Coca Cola, Ramlösa or Pripps lättöl
    public string Drink_name()
    {
        return drinkName;
        //set { drinkName = value; }
    }

    //Property for the drink type e.g. Soda, fizzy water or beer
    public string Drink_type()
    {
        return drinkType;
        //set { drinkType = value; }
    }

    //Property for the drink price in SEK
    public int Drink_price()
    {
        return drinkPrice;
        //set { drinkPrice = value; }
    }

    //Property for the product code e.g. 1, 2 or ...
    public int Product_code()
    {
        return productCode;
        //set { productCode = value; }
    }

    //Property for the product code e.g. 1, 2 or ...
    public int _Product_code
    {
        get { return productCode; }
        set { productCode = value; }
    }

    public override string ToString()
    {
        return string.Format(drinkName + " " + drinkType + " " + drinkPrice + " " + productCode);
        //return string.Format("The beverage {0} is of the type {1} and costs {2} SEK.", drinkName, drinkType, drinkPrice, productCode);
    }
}

class Sodacrate
{
    private soda[] bottles;         //Crate the array bottles from the class soda.
    private int antal_flaskor = 0;  //Keeps tracks on the amount of bottles. 25 is the maximum allowed.

    //Construct
    public Sodacrate()
    {
        bottles = new soda[25];
    }

    public void sort_sodas()
    {
        string drinkName = "";
        int drinkPrice = 0;
        int productCode = 0;
        Array.Sort(bottles, delegate (soda bottle1, soda bottle2) { return bottle1._Product_code.CompareTo(bottle2._Product_code); });
        foreach (var beverage in bottles)
        {
            if (beverage != null)
            {
                drinkName = beverage.Drink_name(); drinkPrice = beverage.Drink_price(); productCode = beverage.Product_code();
                Console.Write(drinkName + " " + drinkPrice + " " + productCode);
            }
        }
    }
}

----------编辑--------------- 感谢您的帮助,我离解决方案越来越近了,我不得不在星期四吃午饭来解决我的问题。

我的排序还是有问题;

//Exception error When I try to have .Product_Name the compiler protests. Invalid token
public void sort_Sodas()
{
    int max = bottles.Length;
    //Outer loop for complete [bottles]
    for (int i = 1; i < max; i++)
    {
        //Inner loop for row by row
        int nrLeft = max - i;
        for (int j = 0; j < (max - i); j++)
        {
            if (bottles[j].Product_code > bottles[j + 1].Product_code)
            {
                int temp = bottles[j].Product_code;
                bottles[j] = bottles[j + 1];
                bottles[j + 1].Product_code = temp;
            }
        }
    }
}

当我想要所有那些对产品组真实的值时,我的线性搜索也只返回一个值。为了更快的实验,我在 Run() 中尝试了一些不同的东西。我将追加当前代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Sodacrate
{
    //Soda - contains the properties for the bottles that go in to the crate
    class Soda : IComparable<Soda>
    {
        string drinkName;
        string drinkType;
        int drinkPrice;
        int productCode;

        //Construct for the beverage
        public Soda(string _drinkName, string _drinkType, int _drinkPrice, int _productCode)
        {
            drinkName = _drinkName;
            drinkType = _drinkType;
            drinkPrice = _drinkPrice;
            productCode = _productCode;
        }

        //Property for the drink name e.g. Coca Cola, Ramlösa or Pripps lättöl
        public string Drink_name
        {
            get { return drinkName; }
            set { drinkName = value; }
        }

        //Property for the drink type e.g. Soda, fizzy water or beer
        public string Drink_type
        {
            get { return drinkType; }
            set { drinkType = value; }
        }

        //Property for the drink price in SEK
        public int Drink_price
        {
            get { return drinkPrice; }
            set { drinkPrice = value; }
        }

        //Property for the product code e.g. 1, 2 or ...
        public int Product_code
        {
            get { return productCode; }
            set { productCode = value; }
        }

        //Override for ToString to get text instead of info about the object
        public override string ToString()
        {
            return string.Format("{0,0} Type {1,-16} Price {2,-10} Code {3, -5} ", drinkName, drinkType, drinkPrice, productCode);
        }

        //Compare to solve my issues with sorting
        public int CompareTo(Soda other)
        {
            if (ReferenceEquals(other, null))
                return 1;

            return drinkName.CompareTo(other.drinkName);
        }

    }

    static class Screen
    {
        // Screen - Generic methods for handling in- and output ======================================= >

        // Methods for screen handling in this object are:
        //
        //  cls()               Clear screen
        //  cup(row, col)       Positions the curser to a position on the console
        //  inKey()             Reads one pressed key (Returned value is : ConsoleKeyInfo)
        //  inStr()             Handles String
        //  inInt()             Handles Int
        //  inFloat()           Handles Float(Singel)
        //  meny()              Menu system , first invariable is Rubrik and 2 to 6 meny choises
        //  addSodaMenu()       The options for adding bottles

        // Clear Screen  ------------------------------------------
        static public void cls()
        {
            Console.Clear();
        }

        // Set Curser Position  ----------------------------------
        static public void cup(int column, int rad)
        {
            Console.SetCursorPosition(column, rad);
        }

        // Key Input --------------------------------------------
        static public ConsoleKeyInfo inKey()
        {
            ConsoleKeyInfo in_key; in_key = Console.ReadKey(); return in_key;
        }

        // String Input -----------------------------------------
        static public string inStr()
        {
            string in_string; in_string = Console.ReadLine(); return in_string;
        }

        // Int Input -------------------------------------------
        static public int inInt()
        {
            int int_in; try { int_in = Int32.Parse(Console.ReadLine()); }
            catch (FormatException) { Console.WriteLine("Input Error \b"); int_in = 0; }
            catch (OverflowException) { Console.WriteLine("Input Owerflow\b"); int_in = 0; }
            return int_in;
        }

        // Float Input -------------------------------------------
        static public float inFloat()
        {
            float float_in; try { float_in = Convert.ToSingle(Console.ReadLine()); }
            catch (FormatException) { Console.WriteLine("Input Error \b"); float_in = 0; }
            catch (OverflowException) { Console.WriteLine("Input Owerflow\b"); float_in = 0; }
            return float_in;
        }

        // Menu ------------------------------------------------
        static public int meny(string rubrik, string m_val1, string m_val2)
        {  // Meny med 2 val ---------------------
            int menSvar; menyRubrik(rubrik); menyRad(m_val1); menyRad(m_val2); menSvar = menyInm();
            return menSvar;
        }

        static public int meny(string rubrik, string m_val1, string m_val2, string m_val3)
        {  // Meny med 3 val ---------------------
            int menSvar; menyRubrik(rubrik); menyRad(m_val1); menyRad(m_val2); menyRad(m_val3); menSvar = menyInm();
            return menSvar;
        }

        static public int meny(string rubrik, string m_val1, string m_val2, string m_val3, string m_val4)
        {  // Meny med 4 val ---------------------
            int menSvar; menyRubrik(rubrik); menyRad(m_val1); menyRad(m_val2); menyRad(m_val3); menyRad(m_val4); menSvar = menyInm();
            return menSvar;
        }

        static public int meny(string rubrik, string m_val1, string m_val2, string m_val3, string m_val4, string m_val5)
        {  // Meny med 5 val ---------------------
            int menSvar; menyRubrik(rubrik); menyRad(m_val1); menyRad(m_val2); menyRad(m_val3); menyRad(m_val4); menyRad(m_val5); menSvar = menyInm();
            return menSvar;
        }

        static public int meny(string rubrik, string m_val1, string m_val2, string m_val3, string m_val4, string m_val5, string m_val6)
        {  // Meny med 6 val ---------------------
            int menSvar; menyRubrik(rubrik); menyRad(m_val1); menyRad(m_val2); menyRad(m_val3); menyRad(m_val4); menyRad(m_val5); ; menyRad(m_val6); menSvar = menyInm();
            return menSvar;
        }

        static void menyRubrik(string rubrik)
        {   // Meny rubrik --------
            cls(); Console.WriteLine("\n\t {0}\n----------------------------------------------------\n", rubrik);
        }

        static void menyRad(string menyVal)
        {   // Meny rad    --------
            Console.WriteLine("\t {0}", menyVal);
        }

        static int menyInm()
        { // Meny inmating ------
            int mVal; Console.Write("\n\t Menyval : "); mVal = inInt(); return mVal;
        }

        // Menu for adding bottles --------------------------------
        static public void addSodaMenu()
        {
            cls();
            Console.WriteLine("\tChoose a beverage please.");
            Console.WriteLine("\t1. Coca Cola");
            Console.WriteLine("\t2. Champis");
            Console.WriteLine("\t3. Grappo");
            Console.WriteLine("\t4. Pripps Blå lättöl");
            Console.WriteLine("\t5. Spendrups lättöl");
            Console.WriteLine("\t6. Ramlösa citron");
            Console.WriteLine("\t7. Vichy Nouveu");
            Console.WriteLine("\t9. Exit to main menu");
            Console.WriteLine("\t--------------------\n");
        }

        // Screen - Slut  <========================================
    } // screen <----

    class Sodacrate
    {
        // Sodacrate - Methods for handling arrays and lists of Soda-objects ======================================= >

        // Methods for Soda handling in this object are:
        //
        //  cls()               Clear screen
        //
        //

        private Soda[] bottles;                                         //Create they array where we store the up to 25 bottles
        private int antal_flaskor = 0;                                  //Keep track of the number of bottles in the crate

        //Inte Klart saknar flera träffar samt exception
        public int find_Soda(string drinkname)
        {
            //Betyg C
            //Beskrivs i läroboken på sidan 147 och framåt (kodexempel på sidan 149)
            //Man ska kunna söka efter ett namn
            //Man kan använda string-metoderna ToLower() eller ToUpper() 
            for (int i = 0; i < bottles.Length; i++)
            {
                if (bottles[i].Drink_name == drinkname)
                    return i;
            }
            return -1;
        }

        //Exception error
        public void sort_Sodas()
        {
            int max = bottles.Length;
            //Outer loop for complete [bottles]
            for (int i = 1; i < max; i++)
            {
                //Inner loop for row by row
                int nrLeft = max - i;
                for (int j = 0; j < (max - i); j++)
                {
                    if (bottles[j].Product_code > bottles[j + 1].Product_code)
                    {
                        int temp = bottles[j].Product_code;
                        bottles[j] = bottles[j + 1];
                        bottles[j + 1].Product_code = temp;
                    }
                }
            }
        }
/*
        //Exception error
        public void sort_Sodas_name()
        {
            int max = bottles.Length;
            //Outer loop for complete [bottles]
            for (int i = 1; i < max; i++)
            {
                //Inner loop for row by row
                int nrLeft = max - i;
                for (int j = 0; j < (max - i); j++)
                {
                    if (bottles[j].Drink_name > bottles[j + 1].Drink_name)
                    {
                        int temp = bottles[j].Drink_name;
                        bottles[j] = bottles[j + 1];
                        bottles[j + 1].Drink_name = temp;
                    }
                }
            }
        }
        */
        //Search for Product code
        public int LinearSearch(int key)
        {

            for (int i = 0; i < bottles.Length; i++)
            {
                if (bottles[i].Product_code == key)
                    return i;
            }
            return -1;
        }

        //Contains the menu to choose from the crates methods
        public void Run()
        {
            bottles[0] = new Soda("Coca Cola", "Soda", 5, 1);
            bottles[1] = new Soda("Champis", "Soda", 6, 1);
            bottles[2] = new Soda("Grappo", "Soda", 4, 1);
            bottles[3] = new Soda("Pripps Blå", "beer", 6, 2);
            bottles[4] = new Soda("Spendrups", "beer", 6, 2);
            bottles[5] = new Soda("Ramlösa", "water", 4, 3);
            bottles[6] = new Soda("Loka", "water", 4, 3);
            bottles[7] = new Soda("Coca Cola", "Soda", 5, 1);

            foreach (var beverage in bottles)
            {
                if (beverage != null)
                    Console.WriteLine(beverage);
            }
            Console.WriteLine("\n\tYou have {0} bottles in your crate:\n\n", bottleCount());

            Console.WriteLine("\nTotal value of the crate\n");
            int total = 0;
            for (int i = 0; i < bottleCount(); i++)
            {
                total = total + (bottles[i].Drink_price);
            }
            /*            int price = 0;                            //Causes exception error
                        foreach(var bottle in bottles)
                        {
                            price = price + bottle.Drink_price;
                        }
                        */
            Console.WriteLine("\tThe total value of the crate is {0} SEK.", total);
            //            Console.WriteLine("\tThe total value of the crate is {0} SEK.", price);

            Screen.inKey();
            Screen.cls();

            int test = 0;
            test = bottles[3].Product_code;
            Console.WriteLine("Product code {0} is in slot {1}", test, 3);
            Screen.inKey();

            Console.WriteLine("Type 1, 2 or 3");
            int prodcode = Screen.inInt();
            Console.WriteLine(LinearSearch(prodcode));

            Console.WriteLine("Product code {0} is in slot {1}", prodcode, (LinearSearch(prodcode)));
            Console.WriteLine(bottles[(LinearSearch(prodcode))]);
            Screen.inKey();
            //            sort_Sodas();                         //Causes Exception error I want it to sort on either product code or product name
            foreach (var beverage in bottles)
            {
                if (beverage != null)
                    Console.WriteLine(beverage);
            }
        }

        //Print the content of the crate to the console
        public void print_crate()
        {
            foreach (var beverage in bottles)
            {
                if (beverage != null)
                    Console.WriteLine(beverage);
                //else
                //Console.WriteLine("Empty slot");
            }
            Console.WriteLine("\n\tYou have {0} bottles in your crate:", bottleCount());
        }

        //Construct, sets the Sodacrate to hold 25 bottles
        public Sodacrate()
        {
            bottles = new Soda[25];
        }

        // Count the ammounts of bottles in crate
        public int bottleCount()
        {
            int cnt = antal_flaskor;
            // Loop though array to get not empty element
            foreach (var beverages in bottles)
            {
                if (beverages != null)
                {
                    cnt++;
                }
            }
            return cnt;
        }

        //Calculates the total value of the bottles in the crate
        public int calc_total()
        {
            int temp = 0;
            for (int i = 0; i < bottleCount(); i++)
            {
                temp = temp + (bottles[i].Drink_price);
            }
            return temp;
        }

        //Adds bottles in the crate.
        public void add_Soda()
        {
            /*Metod för att lägga till en läskflaska
            Om ni har information om både pris, läsktyp och namn
            kan det vara läge att presentera en meny här där man kan
            välja på förutbestämda läskflaskor. Då kan man också rätt enkelt
            göra ett val för att fylla läskbacken med slumpade flaskor
            */

            //I start of with adding 7 bottles to avoid having to add so many bottles testing functions. Remove block before release 
            bottles[0] = new Soda("Coca Cola", "Soda", 5, 1);
            bottles[1] = new Soda("Champis", "Soda", 6, 1);
            bottles[2] = new Soda("Grappo", "Soda", 4, 1);
            bottles[3] = new Soda("Pripps Blå", "lättöl", 6, 2);
            bottles[4] = new Soda("Spendrups", "lättöl", 6, 2);
            bottles[5] = new Soda("Ramlösa citron", "mineralvatten", 4, 3);
            bottles[6] = new Soda("Vichy Nouveu", "mineralvatten", 4, 3);
            //<======================================================================================================= End block

            int beverageIn = 0;                                                 //Creates the menu choice-variable
            while (beverageIn != 9)                                             //Exit this menu by typing 9 - This value should be different if we add more bottle types to add.
            {
                Screen.addSodaMenu();                                           //Calls the menu in the Screen-class
                Console.WriteLine("You have {0} bottles in the crate.\n\nChoose :", bottleCount());
                Screen.cup(8, 13);
                int i = bottleCount();                                          //Keeps track of how many bottles we have in the crate. If the crate is full we get expelled out of this method
                if (i == 25)
                { beverageIn = 9; }
                else beverageIn = Screen.inInt();                               //end

                switch (beverageIn)                                             //Loop for adding bottles to the crate exit by pressing 9
                {
                    case 1:
                        i = bottleCount();
                        bottles[i] = new Soda("Coca Cola", "Soda", 5, 1);
                        i++;
                        break;

                    case 2:
                        i = bottleCount();
                        bottles[i] = new Soda("Champis", "Soda", 6, 1);
                        i++;
                        break;

                    case 3:
                        i = bottleCount();
                        bottles[i] = new Soda("Grappo", "Soda", 4, 1);
                        i++;
                        break;

                    case 4:
                        i = bottleCount();
                        bottles[i] = new Soda("Pripps Blå lättöl", "lättöl", 6, 2);
                        i++;
                        break;

                    case 5:
                        i = bottleCount();
                        bottles[i] = new Soda("Spendrups lättöl", "lättöl", 6, 2);
                        i++;
                        break;

                    case 6:
                        i = bottleCount();
                        bottles[i] = new Soda("Ramlösa citron", "mineralvatten", 4, 3);
                        i++;
                        break;

                    case 7:
                        i = bottleCount();
                        bottles[i] = new Soda("Vichy Nouveu", "mineralvatten", 4, 3);
                        i++;
                        break;

                    case 9:
                        i = bottleCount();
                        if (i == 25)
                        {
                            Console.WriteLine("\tThe crate is full\n\tGoing back to main menu. Press a key: ");
                        }
                        Console.WriteLine("Going back to main menu. Press a key: ");
                        break;

                    default:                                                                    //Default will never kick in as I have error handling in Screen.inInt()
                        Console.WriteLine("Error, pick a number between 1 and 7 or 9 to end.");
                        break;
                }
            }
        }


        // Sodacrate - End  <========================================

        class Program
        {
            public static void Main(string[] args)
            {
                //Skapar ett objekt av klassen Sodacrate som heter Sodacrate
                var Sodacrate = new Sodacrate();
                Sodacrate.Run();
                Console.Write("Press any key to continue . . . ");
                Console.ReadKey(true);
            }
        }
    }
}

【问题讨论】:

  • 为了对某些东西进行排序,您必须以某种方式定义顺序。使用单个数字很明显,但是对于对象,您必须说明这些部分如何组合在一起来定义顺序。你定义过这样的函数吗?
  • 你的老师要你写冒泡排序,所以你无论如何都不能使用Array.Sort
  • “我在一个包含一维的数组上做 [冒泡排序] 都很好”所以让我们看看。
  • @MaciejLos:不,它没有。它可以实现IEquatable,这不是一回事,或者不同的类可以实现IEqualityComparer...或者OP可以传入一个比较委托,就像他们实际做的那样。 ..
  • @MaciejLos:(实际上,我错过了你会使用IComparerIComparable,而不是IEqualityComparer/IEquatable 来进行排序......)

标签: c# arrays sorting


【解决方案1】:

为了对给定类型的对象进行排序,您首先需要知道如何比较两个对象。您可以对数字数组进行排序,因为您知道如何区分 122-10 等等。

您的代码中没有任何地方定义两个sodas(顺便说一下应该是Soda)相互比较。在 c#(和一般的 .NET)中执行此操作的一种方法是让您的类实现一个名为 IComparable&lt;T&gt; 的非常具体的接口:

public interface IComparable<T>
{
    int CompareTo(T other);
}

CompareTo 是诸如Array.Sort 或Linq 的OrderBy 之类的排序算法使用的(如果没有另行说明)。你需要做同样的事情。 T 是一种通用类型,在您的情况下,您有兴趣将苏打水与苏打水进行比较,因此 T 将是 Soda

.NET 中的CompareTo 约定如下:

  • 如果this 等于other,则返回0
  • 如果this 小于other,则返回-1
  • 如果this 大于other,则返回1
  • null 被认为小于任何非 null 值。

您的实现必须遵循此约定,以便内置排序算法起作用。

首先,您需要定义 如何 比较您的 soda。按名字?按价格?一切似乎都是合乎逻辑的选择。如果您的问题指定sodas 应如何比较,则相应地实施比较逻辑,否则选择合理的选项。

我会按名称排序,所以我会执行以下操作:

public class Soda: IComparable<Soda>
{
    ....
    public int CompareTo(Soda other)
    {
        if (ReferenceEquals(other, null))
           return 1;

        return drinkName.CompareTo(other.drinkName);
    }
}

因为string 实现了IComparable&lt;string&gt;,所以实现我自己的比较逻辑非常简单。

好的,现在苏打水知道如何相互比较,像Array.Sort 这样的东西就可以很好地工作了。既然您知道如何比较软饮料,您自己的冒泡排序算法也应该能很好地工作。

另一种选择是实现IComparer&lt;T&gt;,它基本上是一个知道如何比较两个Sodas 的对象。调查一下,尽管在您的情况下,我认为实施 IComparable&lt;T&gt; 是一个更清洁的解决方案。

在另一个页面上,您的代码有一些可以而且应该改进的地方:

  1. soda 应命名为 Soda。类应以大写字母开头(私有嵌套类除外)。
  2. 使用属性而不是 Drink_nameDrink_priceDrink_type 等的方法,更好的是,使用自动实现的属性来减少样板代码。
  3. 去掉Drink表单属性名,类名为Soda,所以Drink几乎是多余的,没有添加任何有用的信息;它只是无缘无故地使属性名称更长。
  4. 如果您坚持保留Drink,请使用驼色外壳并删除_DrinkNameDrinkType等。

【讨论】:

  • 感谢您的帮助,我越来越接近我的解决方案,并且不得不在星期四吃午饭来解决我的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-06
  • 1970-01-01
  • 1970-01-01
  • 2017-09-13
  • 2020-09-04
  • 2015-07-02
相关资源
最近更新 更多