Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

[SOLVED] How to inherit IComparable?

Discussion in 'Scripting' started by shaderbytes, Nov 22, 2015.

  1. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    Hi All

    So I wanted to create a simple class to facilitate indexing ( and array sorting ) a couple of different object types in my scene , the index had to be serialized in the inspector as well so I created this :

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System;
    5.  
    6. public class IndexedDevice : MonoBehaviour ,IComparable<IndexedDevice>{
    7.  
    8.     public int index = 0;
    9.  
    10.     public float getIndex { get{
    11.            
    12.             return index;
    13.            
    14.            
    15.         }
    16.     }
    17.  
    18.     public int CompareTo(IndexedDevice other){
    19.        
    20.         return other.getIndex.CompareTo(this.getIndex);
    21.     }
    22.  
    23.  
    24.  
    25.  
    26. }
    27.  
    Now if you try inherit this class and then push the object to an Array and call sort , it complains about the current class not having the interface for sorting. I know I could probably cast the current object types into an array typed to the IndexedDevice type and it would work but I wondering why it doesnt just work without the casting.. is there some other clever way to do this?

    thanks in advance
     
  2. btft

    btft

    Joined:
    Aug 11, 2015
    Posts:
    14
    Could you provide an example on how you add elements to collection and call sort on them?
    What is the type stored in collection? IComparable<IndexedDevice> or IndexedDevice?
    For collection storing IndexedDevice it should be just fine:

    Code (CSharp):
    1. var list = new List<IndexedDevice>();
    2. list.Add(new IndexedDevice());
    3. list.Add(new IndexedDevice());
    4. list.Add(new IndexedDevice());
    5. //This should work just fine:
    6. list.Sort();
     
    Last edited: Nov 22, 2015
    coward likes this.
  3. coward

    coward

    Joined:
    Jan 9, 2013
    Posts:
    21
    +1 for this. Using the existing classes will help you immensely.

    If you don't need access to each item in the collection by index, you can just pass around an IEnumerable<IndexedDevice> and use the linq extension methods (orderby and orderbydescending)
     
  4. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    The array is not typed to IndexedDevice , I know that would work, I want to have several different class types inherit IndexedDevice and hence inherit the index and IComparable interface .. or at least that was my plan which doesnt work
     
  5. coward

    coward

    Joined:
    Jan 9, 2013
    Posts:
    21
    Are you planning on comparing against fields on the base IndexedDevice class, or will the inherited classes have different fields to compare? You could look at overriding the Equals and GetHashCode methods (get hash code is used my most ootb sorting in .NET at least).
     
  6. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    these classes are all scene objects that extend monobehaviour, I could just implement this interface on each class ( in fact that is what I did now because I could not get this to work ), or create a few custom typed Comparar classes .. but I was just trying to make a simple class to make this be inherited boilerplate type code... so that any monobehaviour class I plan to need to have simple index and sorting can just extend it..
     
  7. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    The index needs to be a field because I want to set it in the inspector as needed , hence if you look at my code example you will see a field and a get method for the compareto function
     
  8. coward

    coward

    Joined:
    Jan 9, 2013
    Posts:
    21
    What is the result of the sorting? Something in the UI? Your array can be typed to indexed device and still have the inherited classed inserted into it:

    Code (CSharp):
    1.  
    2. public class IndexedDevice
    3. {
    4.         public int _index = 0;
    5.         public int Index { get { return Index; } }
    6. }
    7.  
    8. public class AnotherDevice : IndexedDevice { }
    9. public class YetAnotherDevice : IndexedDevice { }
    10.  
    11. public class Sorter
    12. {
    13.     public void Sort()
    14.     {
    15.         var devices = new IndexedDevice[] { new AnotherDevice(), new YetAnotherDevice() };
    16.         var desc = devices.OrderByDescending(i => i.Index);
    17.     }
    18. }
    Of course, you can't new up MonoBehaviours that way, but it's an example ;)
     
  9. btft

    btft

    Joined:
    Aug 11, 2015
    Posts:
    14
    I'll use console application as unity script is way to much fuss for such example:

    Code (CSharp):
    1. public class Program
    2.     {
    3.         static void Main(string[] args)
    4.         {
    5.             List<IndexedDevice> devices = new List<IndexedDevice>();
    6.             devices.Add(new IndexedDevice() {index = 1});
    7.             devices.Add(new TopIndexedDevice() { index = 22 });
    8.             devices.Add(new IndexedDevice() { index = 17 });
    9.             devices.Add(new LowIndexedDevice() { index = 100 });
    10.             devices.Add(new IndexedDevice() { index = -1 });
    11.  
    12.             devices.Sort();
    13.             foreach (var device in devices)
    14.                 Console.WriteLine(device.index);
    15.  
    16.             Console.ReadKey();
    17.         }
    18.     }
    19.  
    20.     public class IndexedDevice : IComparable<IndexedDevice>
    21.     {
    22.  
    23.         public int index = 0;
    24.  
    25.         public float getIndex
    26.         {
    27.             get { return index; }
    28.         }
    29.  
    30.         public int CompareTo(IndexedDevice other)
    31.         {
    32.  
    33.             return other.getIndex.CompareTo(this.getIndex);
    34.         }
    35.     }
    36.  
    37.     public class TopIndexedDevice : IndexedDevice
    38.     {
    39.         public string Name;
    40.     }
    41.  
    42.     public class LowIndexedDevice : IndexedDevice
    43.     {
    44.         public float Delta;
    45.     }
    46. }
    Result:
    -1, 1, 17, 22, 100

    Seems that inheritance works just fine. :)
     
  10. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    thanks guys , but you keep typing the List/Array to the base type , I understand that will work, what I was/am trying to achieve is to have this functionality derived on the extended type so

    Code (csharp):
    1.  
    2. public class EmissionObjectBase : IndexedDevice{ ..blah blah..}
    3.  
    and then in another class ..

    Code (csharp):
    1.  
    2. public class EmissionObjectSequenceController : MonoBehaviour {
    3.  
    4.     public EmissionObjectBase[] EOPointLights;
    5.     public EmissionObjectBase[] EOMaterialLights;
    6.     public EmissionObjectBase[] EOShaderLights;
    7.  
    8.     private void ApplicationBootStart () {
    9.         EOPointLights = FindObjectsOfType<EmissionObjectPointLight> ();
    10.         EOMaterialLights = FindObjectsOfType<EmissionObjectMaterial> ();
    11.         EOShaderLights = FindObjectsOfType<EmissionObjectShaderLight> ();
    12.        
    13.         Array.Sort (EOPointLights);
    14.         Array.Sort (EOMaterialLights);
    15.         Array.Sort (EOShaderLights);
    16.        
    17.         Array.Reverse (EOPointLights);
    18.         Array.Reverse (EOMaterialLights);
    19.         Array.Reverse (EOShaderLights);
    20.  
    21.     }
    22. }
    23.  
     
  11. coward

    coward

    Joined:
    Jan 9, 2013
    Posts:
    21
    You can still use the same concepts because it inherits from IndexedDevice:

    Code (csharp):
    1. var descending = EOPointLights.OrderByDescending(i => i.Index);
    Oh, but of course that doesn't sort the array itself...

    I see the dilemma with the IComparable now. Array.Sort is not a nice method! Will have a look into it...
     
    Last edited: Nov 22, 2015
  12. btft

    btft

    Joined:
    Aug 11, 2015
    Posts:
    14
    This will still work. No idea what you're asking for now...


    Code (CSharp):
    1.     public class Program
    2.     {
    3.         static void Main(string[] args)
    4.         {
    5.             List<TopIndexedDevice> devices = new List<TopIndexedDevice>();
    6.             devices.Add(new Top1IndexedDevice() {index = 1});
    7.             devices.Add(new TopIndexedDevice() { index = 22 });
    8.             devices.Add(new TopIndexedDevice() { index = 17 });
    9.             devices.Add(new Low1IndexedDevice() { index = 100 });
    10.             devices.Add(new Low1IndexedDevice() { index = -1 });
    11.  
    12.             devices.Sort();
    13.             foreach (var device in devices)
    14.                 Console.WriteLine(device.index);
    15.  
    16.             Console.ReadKey();
    17.         }
    18.     }
    19.  
    20.     public class IndexedDevice : IComparable<IndexedDevice>
    21.     {
    22.  
    23.         public int index = 0;
    24.  
    25.         public float getIndex
    26.         {
    27.             get { return index; }
    28.         }
    29.  
    30.         public int CompareTo(IndexedDevice other)
    31.         {
    32.  
    33.             return other.getIndex.CompareTo(this.getIndex);
    34.         }
    35.     }
    36.  
    37.     public class TopIndexedDevice : IndexedDevice
    38.     {
    39.         public string Name;
    40.     }
    41.  
    42.     public class LowIndexedDevice : IndexedDevice
    43.     {
    44.         public float Delta;
    45.     }
    46.  
    47.     public class Top1IndexedDevice : TopIndexedDevice
    48.     {
    49.        
    50.     }
    51.  
    52.     public class Low1IndexedDevice : TopIndexedDevice
    53.     {
    54.        
    55.     }
     
  13. coward

    coward

    Joined:
    Jan 9, 2013
    Posts:
    21
    If you want to go down the IComparable route, implement both IComparables. I have introduced an interface, just because, but you don't need to:

    Code (CSharp):
    1.  
    2. public interface IIndexedDevice
    3. {
    4.         int Index { get; }
    5. }
    6.  
    7. public class IndexedDevice : IIndexedDevice, IComparable<IIndexedDevice>, IComparable
    8. {
    9.         public int _index = 0;
    10.         public int Index { get { return _index; } }
    11.  
    12.         public int CompareTo(object obj)
    13.         {
    14.             return CompareTo(obj as IIndexedDevice);
    15.         }
    16.  
    17.         public int CompareTo(IIndexedDevice other)
    18.         {
    19.             if (other == null)
    20.                 return -1;
    21.  
    22.             if (other == this)
    23.                 return 0;
    24.  
    25.             return this.Index - other.Index;
    26.         }
    27. }
    28.  
     
    Tapuck likes this.
  14. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    Thanks but I just solved it via just writing a comparar :

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System;
    5. using System.Collections.Generic;
    6.  
    7. public class IndexedDeviceComparar: Comparer<IndexedDevice>{
    8.     public override  int Compare(IndexedDevice x, IndexedDevice y)   {
    9.         if(x == null){
    10.             if(y == null){
    11.                 return 0;
    12.             }
    13.             return -1;
    14.         }
    15.         if(y == null){
    16.             return 1;
    17.         }
    18.      
    19.         if(x.index == y.index){
    20.             return 0;
    21.         }else if(x.index>y.index){
    22.             return 1;
    23.         }
    24.         return -1;
    25.     }
    26. }
    27.  
    28.  
    So now my index class has nothing more than just a field :

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System;
    5.  
    6. public class IndexedDevice : MonoBehaviour {
    7.  
    8.     public int index = 0;
    9.  
    10.  
    11.  
    12.  
    13. }
    14.  
    15.  
    16.  
    and now in the sequencer I just push the comparar into array.sort :

    Code (csharp):
    1.  
    2. private void ApplicationBootStart () {
    3.         EOPointLights = FindObjectsOfType<EmissionObjectPointLight>();
    4.         EOMaterialLights = FindObjectsOfType<EmissionObjectMaterial>();
    5.         EOShaderLights = FindObjectsOfType<EmissionObjectShaderLight>();
    6.  
    7.         IndexedDeviceComparar IDC = new IndexedDeviceComparar ();
    8.  
    9.         Array.Sort (EOPointLights,IDC);
    10.         Array.Sort (EOMaterialLights,IDC);
    11.         Array.Sort (EOShaderLights,IDC);
    12.  
    13.  
    14.  
    15.  
    16.         EOObjectsCollections.Add (EOPointLights);
    17.         EOObjectsCollections.Add (EOMaterialLights);
    18.         EOObjectsCollections.Add (EOShaderLights);
    19.  
    20.  
    21.         CreateSequences ();
    22.  
    23.  
    24.      
    25.     }
    26.  
    27.  
    28.  
    Whats bizarre is that C# understands to derive the type when passing in an comparar that is typed to IndexedDevice on my array of objects that extended it ... yet the internal sort method cannot do the same thing..?
     
  15. coward

    coward

    Joined:
    Jan 9, 2013
    Posts:
    21
    Because it uses Type Inference in the Array.Sort method based on the type of the array. You can declare it yourself and save some code with Array.Sort<IIndexedDevice>(EOPointLights). With IIndexedDevice being the generic parameter of the IComparable declaration.
     
    shaderbytes likes this.
  16. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    Strangely enough that doesnt work , well I cant say if there is more support on List.Sort than on Array.Sort ..My code was using Array.Sort.. I tried it with the same logic you have there and unity gave me a compiler error saying the extended class does not implement the IComparable interface ,anyway see above I resorted to just using a comparar class instead
     
  17. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    mmm ... let me go test that an see , thanks ;)
     
  18. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    @coward for the win!! and thanks @btft for your insight as well

    so after alot of back and forth the simple solution was as coward mentioned :

    Code (csharp):
    1.  
    2. Array.Sort<IndexedDevice> (EOPointLights);
    3.  
    4.  
    I knew there had to be a simple solution for this thanks a million!