Laden...

Operatoren debuggen - Haltepunkt wird nicht erreicht

Erstellt von GG71 vor 12 Jahren Letzter Beitrag vor 12 Jahren 1.596 Views
G
GG71 Themenstarter:in
75 Beiträge seit 2007
vor 12 Jahren
Operatoren debuggen - Haltepunkt wird nicht erreicht

Hallo Leute,

ich kann meine Operator-Überladung nicht debuggen,
wenn ich bei

Assert.IsTrue(A == B);

oder im Operator selbst
ein Haltepunkt setze und versuche mit F11 reizukommen geht der Debugger gleich weiter... ?!?
Die VS-Debugging-Option [] Eigenschaften und Operatoren überspringen (nur verwaltet)_ ist nicht_aktiv!

Woran könnte es sonst noch liegen?!?

Ciao:
GG 😉

Ciao:
GG 😉

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo,

ist die Klasse oder Methode mit einem DebuggerXXX-Attribut versehen? ZB mit dem DebuggerNonUserCode-Attribut.

Versuch mal innerhalb des Operators einen Breakpoint per Code zu erstellen, also


System.Diagnostics.Debugger.Break();

als 1. Code-Zeile.

Sonst zeig mal deinen Code.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

G
GG71 Themenstarter:in
75 Beiträge seit 2007
vor 12 Jahren

Hi,

auch Debugger.Break tut nicht, denn es wird gerade gar nicht aufgerufen:


        /// <summary>TestExemplare (IEquatable&lt;T&gt;-implementierende Klassen mit TestIEquatableAttribute) testen.</summary>
        /// <param name="Specimen">TestExemplar</param>
        /// <returns>true</returns>
        public bool TestInstance<T>(TestSpecimen<T> Specimen) where T : class, IEquatable<T>
        {
            this.TestContext.WriteLine("TestIEquatable.TestInstance: Type [{0}]", Specimen.SpecimenType.FullName);

            // TODO #GG-20110512-1333#: false kann ja garnicht geben, da überall mit Assert geprüft wird, was zum Abbruch führt!
            bool success = true;

            T A = null;
            T B = null;

            A = Specimen.Object0;
            this.TestContext.WriteLine("A.Type: {0}", A.GetType());
            this.TestContext.WriteLine("A.HashCode: {0}", A.GetHashCode());

            B = Specimen.EqualsObject0;
            this.TestContext.WriteLine("B.Type: {0}", B.GetType());
            this.TestContext.WriteLine("B.HashCode: {0}", B.GetHashCode());

            MethodInfo StaticEqualsMethod = Specimen.SpecimenType.GetMethod("Equals",
                                          BindingFlags.Public | BindingFlags.Static,
                                          null,
                                          new Type[] { typeof(object), typeof(object) },
                                          null);
            #region +[X] Part 1: A == B -und- B == A

            this.TestContext.WriteLine("Part 1: A == B -und- B == A");

            Assert.AreEqual(A.GetHashCode(), B.GetHashCode());
            Assert.IsTrue(A.Equals(B));
            Assert.IsTrue(B.Equals(A));
            Assert.IsTrue((bool)StaticEqualsMethod.Invoke(null, new object[] { A, B }));
            Assert.IsTrue((bool)StaticEqualsMethod.Invoke(null, new object[] { A, B }));

            // BUG #GG-20110512-1459#: Es wird ein Referenzvergleich durchgeführt!!!
            Assert.IsTrue(A == B);
            Assert.IsTrue(B == A);
            Assert.IsFalse(A != B);
            Assert.IsFalse(B != A);

            #endregion
...

Wie bringe ich den dazu die Operatoren von T zu nehmen???

Wenn ich die Objekte nicht generisch erzeuge geht es ja auch.

Ciao:
GG 😉

Ciao:
GG 😉

5.742 Beiträge seit 2007
vor 12 Jahren

Hallo GG71,

implementiere IEquantable<T> und nutze das entsprechend.

G
GG71 Themenstarter:in
75 Beiträge seit 2007
vor 12 Jahren

Moin,

na klar habe ich es implementiert, genau das will ich doch testen/debugen.

Hier mal 1x Beispiel:


using System;
using BlaBla.Testing;

namespace BlaBla.XsdBase
{
    /// <summary>
    /// Partielle Erweiterung der aus XSD per SvcUtil generierte Klasse um die IEquatable&lt;T&gt;-Schnittstelle.
    /// <see cref="XsdFull.bat"/>
    /// <see cref="XsdFull.designer.cs"/>
    /// <see cref="http://msdn.microsoft.com/de-de/library/ms131187.aspx"/>
    /// </summary>
    [TestIEquatable("Von", 42)]
    public partial class UmfangTyp : IEquatable<UmfangTyp>
    {
        #region IEquatable

        /// <summary>Implementierung von UmfangTyp.Equals(UmfangTyp)</summary>
        /// <param name="Other">Auf Gleichheit zu prüfende UmfangTyp-Instanz</param>
        /// <returns>
        /// true, wenn die Instanzen gleich sind -oder- 
        /// false, wenn other null, nicht UmfangTyp oder ungleich ist
        /// </returns>
        public bool Equals(UmfangTyp Other)
        {
            return (false == ReferenceEquals(Other, null)) &&
                   (Other.GetType() == this.GetType()) &&
                   (Other.Von == this.Von) &&
                   (Other.Bis == this.Bis) &&
                   (Other.Gesamt == this.Gesamt);
        }


        /// <summary>UmfangTyp-spezifische Abwandlung von Object.Equals(Object)
        /// <see cref="UmfangTyp.Equals(UmfangTyp)"/></summary>
        /// <param name="Other">Auf Gleichheit zu prüfende Object-Instanz</param>
        /// <returns>
        /// false, wenn other null oder vom abweichenden Type ist -oder-
        /// true|false entsprechend Equals(UmfangTyp)
        /// </returns>
        public override bool Equals(object Other)
        {
            if (ReferenceEquals(Other, null))
                return false;

            else if (typeof(UmfangTyp) == Other.GetType())
                return this.Equals(Other as UmfangTyp);

            else
                return false;
        }


        /// <summary>UmfangTyp-spezifische Abwandlung von Object.GetHashCode()</summary>
        /// <returns>HashCode für Schnellvergleich zwei UmfangTyp-Instanzen</returns>
        public override int GetHashCode()
        {
            return this.Von.GetHashCode() ^
                   this.Bis.GetHashCode() ^
                   this.Gesamt.GetHashCode();
        }


        /// <summary>UmfangTyp-spezifische Abwandlung von Object.Equals(object, object)
        /// <see cref="Equals(object)"/></summary>
        /// <param name="A">object A</param>
        /// <param name="B">object B</param>
        /// <returns>
        /// true, wenn A und B gleichen Objektreferenz haben -oder- 
        /// false, wenn A oder B null ist -oder-
        /// true|false entsprechend A.Equals(B)
        /// </returns>
        public static new bool Equals(object A, object B)
        {
            if (ReferenceEquals(A, B))
                return true;

            if (ReferenceEquals(A, null) || ReferenceEquals(B, null))
                return false;
            
            return A.Equals(B);
        }


        /// <summary>UmfangTyp-spezifische Abwandlung des Object == Operators</summary>
        /// <param name="A">UmfangTyp A</param>
        /// <param name="B">UmfangTyp B</param>
        /// <returns>true|false entsprechend UmfangTyp.Equals(A, B)</returns>
        public static bool operator ==(UmfangTyp A, UmfangTyp B)
        {
            return UmfangTyp.Equals(A, B);
        }


        /// <summary>UmfangTyp-spezifische Abwandlung des Object != Operators</summary>
        /// <param name="A">UmfangTyp A</param>
        /// <param name="B">UmfangTyp B</param>
        /// <returns>true|false entgegengesetzt UmfangTyp.Equals(A, B)</returns>
        public static bool operator !=(UmfangTyp A, UmfangTyp B)
        {
            return !UmfangTyp.Equals(A, B);
        }

        #endregion
    }
}

Ciao:
GG 😉

Ciao:
GG 😉

G
GG71 Themenstarter:in
75 Beiträge seit 2007
vor 12 Jahren

Noch was merkwürdiges:
Wenn ich um den Test zu testen das hier mal temporär einfüge:

BlaBla.XsdBase.UmfangTyp u0 = A as BlaBla.XsdBase.UmfangTyp;
BlaBla.XsdBase.UmfangTyp u1 = B as BlaBla.XsdBase.UmfangTyp;
Assert.IsTrue(u0 == u1);

das funktioniert einwandfrei bzw. dort komme ich mit F11 auch nicht in den "==" Operator aber die Code-Abdeckung zeigt (blau), dass es lief.

Ist natürlich keine Lösung, denn ich brauche die Testmethode generisch, dass ich 1..n Implementierung testen kann.

Ciao:
GG 😉

Ciao:
GG 😉

1.378 Beiträge seit 2006
vor 12 Jahren

Das wird auch so nicht funktionieren. Wie du schon selbst irgendwo oben geschrieben hast, sind Operatoren statische Funktionen. Statische Funktionen unterliegen daher auch nicht den klassischen Vererbungsprinzipien sodass der Operator der in einer SubKlasse definiert wurde nicht aufgerufen wird, wenn er auf eine Basisklasse angewendet wird. Der Typ T in deinem Code ist abstrakter als der Typ der den Operator implementiert weswegen dieser nicht aufgerufen wird.


    class Program
    {
        static void Main(string[] args)
        {
            Sub a1 = new Sub();
            Sub b1 = new Sub();

            Console.WriteLine(a1 == b1); //prints "Operator in Sub called: ..."

            Test a2 = new Sub();
            Test b2 = new Sub();

            Console.WriteLine(a2 == b2); //prints "Operator in Test called: ..."

            object a3 = new Sub();
            object b3 = new Sub();

            Console.WriteLine(a3 == b3); //prints "..."
        }
    }

    public class Test
    {
        public static bool operator ==(Test test1, Test test2)
        {
            Console.Write("Operator in Test called: ");
            return Equals(test1, test2);
        }

        public static bool operator !=(Test test1, Test test2)
        {
            return !(test1 == test2);
        }
    }

    public class Sub : Test
    {
        public static bool operator ==(Sub test1, Sub test2)
        {
            Console.Write("Operator in Sub called: ");
            return Equals(test1, test2);
        }

        public static bool operator !=(Sub test1, Sub test2)
        {
            return !(test1 == test2);
        }
    }

Lg XXX

//Edit: Ich denke das es auch eine schlechte Angewohnheit ist mit == zu arbeiten(wie ich es selber auch permanent mache) eben, weil == oft irreführend sein kann.

49.485 Beiträge seit 2005
vor 12 Jahren

Hallo zusammen,

Wie du schon selbst irgendwo oben geschrieben hast, sind Operatoren statische Funktionen. Statische Funktionen unterliegen daher auch nicht den klassischen Vererbungsprinzipien sodass der Operator der in einer SubKlasse definiert wurde nicht aufgerufen wird, wenn er auf eine Basisklasse angewendet wird.

genau. Deshalb gibt es auch das virtuelle Equals in jeder Klasse. Equals und operator== sollten immer passend zueinander implementiert sein. Für semantische Korrektheit ist es dabei sinnvoll, operator== durch Aufruf von Equals zu implementieren (allerdings vorher Null-Referenzen abfangen und nötigenfalls auf Typgleichheit der Objekte testen).

herbivore

G
GG71 Themenstarter:in
75 Beiträge seit 2007
vor 12 Jahren

Hallo Leute,

genau das hebe ich doch gemacht:
Weil ich Equals(T Other) überschrieben habe musste ich dementsprechend
Equals(object Other) -und- GetHashCode() -und- T.Equals(object A, object B) -und- operator ==(T A, T B) -und- operator !=(T A, T B) überschreiben müssen.

@xxxprod: Wie kann ich mit MethodInfo auf die statische Operatoren zugreifen?

Ciao:
GG 😉

Ciao:
GG 😉

49.485 Beiträge seit 2005
vor 12 Jahren

Hallo GG71,

das scheint mir kein Debugging-Problem zu sein, sondern sobald where T: class gefordert wird, darf man den -Operator auf Variablen und Ausdrücke des Typs T anwenden. Dies führt aber nicht zu einem Aufruf von T.operator , weil durch where T: class weder gefordert noch sichergestellt wird, dass es einen solchen Operator gibt, sondern es wird automatisch ein Referenzvergleich durchgeführt, wie du das ja auch festgestellt hast.

Meine Anmerkungen zu Equals werden dadurch nicht falsch, aber es mangelt ja schon an dem Aufruf des passenden Operators, so dass das in diesem Fall nichts hilft.

Wenn man den korrekten Vergleich durchführen will, muss man direkt Equals aufrufen.

herbivore

1.378 Beiträge seit 2006
vor 12 Jahren

Dr. Google empfiehlt folgenden Thread: Reflection and Operator Overloads in C#

Alternativ könntest du einfach ein Testprojekt schreiben in dem du einen Operator deklarierst und dich per Reflection und GetMethods zum Ziel durchhangelst?

Lg, XXX

//Edit: was aber nichts daran ändert, dass wenn deine Klasse irgendwo generisch verwendet wird, der Operator nie greifen wird. Insofern ist es wohl sinnvoller das ganze mit der Operatorüberladung nochmals zu überdenken. Evt. ist deine Klasse besser als Struct implementiert bei dem automatisch beim Vergleich alle Member verglichen werden.

G
GG71 Themenstarter:in
75 Beiträge seit 2007
vor 12 Jahren

Hallo Leute,

so gehts:

MethodInfo StaticOpEqualityMethod = Specimen.SpecimenType.GetMethod("op_Equality",
																	BindingFlags.Public | BindingFlags.Static,
																	null,
																	new Type[] { typeof(T), typeof(T) },
																	null);
MethodInfo StaticOpInequalityMethod = Specimen.SpecimenType.GetMethod("op_Inequality",
																	  BindingFlags.Public | BindingFlags.Static,
																	  null,
																	  new Type[] { typeof(T), typeof(T) },
																	  null);
#region +[_] Part 1: A == B -und- B == A

this.TestContext.WriteLine("Part 1: A == B -und- B == A");

Assert.AreEqual(A.GetHashCode(), B.GetHashCode());
Assert.IsTrue(A.Equals(B));
Assert.IsTrue(B.Equals(A));
Assert.IsTrue((bool)StaticEqualsMethod.Invoke(null, new object[] { A, B }));
Assert.IsTrue((bool)StaticEqualsMethod.Invoke(null, new object[] { A, B }));
// BUG #GG-20110512-1459#: Es wird ein Referenzvergleich durchgeführt!!!
//Assert.IsTrue(A == B);
//Assert.IsTrue(B == A);
//Assert.IsFalse(A != B);
//Assert.IsFalse(B != A);
Assert.IsTrue((bool)StaticOpEqualityMethod.Invoke(null, new object[] { A, B }));
Assert.IsTrue((bool)StaticOpEqualityMethod.Invoke(null, new object[] { A, B }));
Assert.IsFalse((bool)StaticOpInequalityMethod.Invoke(null, new object[] { A, B }));
Assert.IsFalse((bool)StaticOpInequalityMethod.Invoke(null, new object[] { A, B }));

Debuggen geht immer noch nicht: Am Haltepunkt im "==" Operator hält er kurz aber mit F11 springt er gleich wieder zum UTest zurück statt Einzelschritt zu machen... ?!?

Ciao:
GG 😉

Ciao:
GG 😉