Usuwanie z listy elementów własnego typu za pomocą Remove(T)

0

Witam

Mam zdefiniowany typ

reservation { 
int NumberOfGroup ;
int TypeOfReservation;
}

Mam listę takich obiektów, i chciałbym żeby polecenie list.Remove(new reservation(int,int)) działało tak jak dla typów wbudowanych. Z tego co wyczytałem, listy są porównywane za pomocą EqualityComparer(Of T).Default, więc zgodnie z dokumentacja MSDN utworzyłem następującą klasę

 public class ReservationComparer : EqualityComparer<reservation>
            {
                public override int GetHashCode(reservation r)
                {
                    int Chcode = r.NumberOfGroup + r.TypeOfReservation;
                    return Chcode.GetHashCode();
                }
                public override bool Equals(reservation r1, reservation r2)
                {
                    if (r1.NumberOfGroup == r2.NumberOfGroup && r1.TypeOfReservation == r2.TypeOfReservation)
                        return true;
                    else
                        return false;
                }
            }
 

Ale to chyba nie o to chodziło, spróbowałem przeciążyć operator == ale to też nie pomogło.
Co robię źle?

 public static bool operator ==(reservation r1, reservation r2)
            {
                if (r1.NumberOfGroup == r2.NumberOfGroup && r1.TypeOfReservation == r2.TypeOfReservation)
                    return true;
                else
                    return false;
            }
            public static bool operator !=(reservation r1, reservation r2)
            {
                if (r1.NumberOfGroup == r2.NumberOfGroup && r1.TypeOfReservation == r2.TypeOfReservation)
                    return false;
                else
                    return true;
            }
            public override int GetHashCode()
            {
                return base.GetHashCode();
            }
            public override bool Equals(object obj)
            {
                return base.Equals(obj);
            } 
0

No niestety ale jeżeli chcesz usunąć z listy element, to musisz podać jego referencję. Lista którą tworzysz nie przechowuje samych zmiennych ale coś jakby wskaźniki do nich. Listy typów wbudowanych przechowują wartości i wtedy coś takiego działa.

0

A poza tym:

public override bool Equals(reservation r1, reservation r2)
{
    if (r1.NumberOfGroup == r2.NumberOfGroup && r1.TypeOfReservation == r2.TypeOfReservation)
        return true;
    else
        return false;
}
 

Ładniej zapisać tak:

public override bool Equals(reservation r1, reservation r2)
{
    return r1.NumberOfGroup == r2.NumberOfGroup && r1.TypeOfReservation == r2.TypeOfReservation;
}
 
1
protected override bool Equals(object obj)
            {
                if(obj == null)
                      return false;
                if(!(obj is reservation))
                      return false;
                Reservation r = obj as Reservation;
                return (r.NumberOfGroup == this.NumberOfGroup && r.TypeOfReservation == this.TypeOfReservation);
            } 
 
0

Dzięki za odpowiedzi, po przeciążeniu tak jak napisał j_s_r_n wszystko działa tak jak należy, więc jednak się da :)

0
j_s_r_n napisał(a)
protected override bool Equals(object obj)
            {
                if(obj == null)
                      return false;
                if(!(obj is reservation))
                      return false;
                Reservation r = obj as Reservation;
                return (r.NumberOfGroup == this.NumberOfGroup && r.TypeOfReservation == this.TypeOfReservation);
            } 
 

Po co używać as, jeśli wcześniej zostało użyte is?

0

obj jest typu object, więc potrzebny jest cast; "is" zwraca tylko bool.
Jeśli pytasz czemu robię cast używając operatora "as" a nie prefixem "(Reservation)obj;", to ten pierwszy jest bardziej wydajny i szczerze mówiąc, jest dla mnie bardziej naturalny w c#(jeśli się wie jak działa).

5
j_s_r_n napisał(a)

Jeśli pytasz czemu robię cast używając operatora "as" a nie prefixem "(Reservation)obj;", to ten pierwszy jest bardziej wydajny i szczerze mówiąc, jest dla mnie bardziej naturalny w c#(jeśli się wie jak działa).

Bardziej wydajny? Skąd ten pomysł? Z artykułu z CodeProject sprzed 8 lat, czyli czasów .NET 1.1?

Mój kod testujący:

class A
{
    public int X { get; set; }
}

class Program
{
    static double sum = 0;

    static void Main(string[] args)
    {
        int repeat = int.MaxValue;
        object a = new A() { X = 7 };

        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < repeat; i++)
        {
            Cast(a);
        }
        sw.Stop();
        Console.WriteLine(sum);
        Console.WriteLine("Cast: {0}", sw.ElapsedMilliseconds);

        sw.Restart();
        for (int i = 0; i < repeat; i++)
        {
            As(a);
        }
        sw.Stop();
        Console.WriteLine(sum);
        Console.WriteLine("As: {0}", sw.ElapsedMilliseconds);
    }

    private static void Cast(object obj)
    {
        A a = (A)obj;
        sum += a.X;
    }

    private static void As(object obj)
    {
        A a = obj as A;
        sum += a.X;
    }
}

Wynik:

15032385529
Cast: 8473
30064771058
As: 9241

Czyli tak jakby samo rzutowanie było wydajniejsze.

Za to użycie as i sprawdzenie czy wynik nie jest null jest wydajniejsze niż duet is i rzutowanie. Ty jednak tego nie robisz, Ty używasz is i as, co sensu żadnego nie ma, bo po co dwa razy sprawdzać to samo? Dwa razy wywołujesz tę samą instrukcję IL isinst, więc jasne, że będzie to wolniejsze niż as, ale także niż is i zwykłe rzutowanie.

 
class Program
{
    static double sum = 0;

    static void Main(string[] args)
    {
        int repeat = int.MaxValue;
        object a = new A() { X = 7 };

        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < repeat; i++)
        {
            IsCast(a);
        }
        sw.Stop();
        Console.WriteLine(sum);
        Console.WriteLine("IsCast: {0}", sw.ElapsedMilliseconds);

        sw.Restart();
        for (int i = 0; i < repeat; i++)
        {
            As(a);
        }
        sw.Stop();
        Console.WriteLine(sum);
        Console.WriteLine("As: {0}", sw.ElapsedMilliseconds);

        sw.Restart();
        for (int i = 0; i < repeat; i++)
        {
            IsAs(a);
        }
        sw.Stop();
        Console.WriteLine(sum);
        Console.WriteLine("IsAs: {0}", sw.ElapsedMilliseconds);
    }

    private static void IsCast(object obj)
    {
        bool b = obj is A;
        if (b)
        {
            A a = (A)obj;
            sum += a.X;
        }
    }

    private static void As(object obj)
    {
        A a = obj as A;
        if (a != null)
        {
            sum += a.X;
        }
    }

    private static void IsAs(object obj)
    {
        bool b = obj is A;
        if (b)
        {
            A a = obj as A;
            sum += a.X;
        }
    }
}

Wynik:

15032385529
IsCast: 13131
30064771058
As: 10015
45097156587
IsAs: 14630

A wracając do tematu, samo użycie is w Equals jest błędem. Przez to, operacja obiektKlasyDziedziczącej.Equals(obiektKlasyBazowej) może zwrócić wartość true, co przecież nie ma sensu! A ponadto komplikuje nadpisywanie tej metody w klasach dziedziczących.

W Equals, zarówno przy użyciu snippetu w VS, jak i w przykładach na MSDN nie stosuje się is ani as tylko po prostu GetType(). Na przykład:

public override bool Equals(object obj)
{
    if (obj == null || this.GetType() != obj.GetType())
        return false;

    A a = (A)obj;
    return a.X == this.X;
}
0

Mea culpa. Jakbym miał konto to bym dał +1.
Takie posty powinny być gdzieś wyróżniane.

1 użytkowników online, w tym zalogowanych: 0, gości: 1