Stilfrage zu static

  • C#

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von jvbsl.

    Stilfrage zu static

    Hallo Forum,

    ich habe nach Guidelines gesucht, allerdings keine gefunden. Angenommen ich habe z.B zwei Vektoren einer Vektorklasse. Ist es dann "schöner" eine Methode der Art

    VB.NET-Quellcode

    1. public static bool IsOrthogonal(Vector3 one, Vector3 two)...


    zu verwenden oder sollte man eher auf

    VB.NET-Quellcode

    1. public bool IsOrthogonal(Vector3 other)...


    zurückgreifen. Oder ist es einfach Geschmackssache?

    LG

    TheVBTutorialsVB schrieb:

    ich habe nach Guidelines gesucht, allerdings keine gefunden.
    Wenn ich die Codeanalyse vom Studio durchführe, kommt immer dann der Hinweis, eine Prozedur nach static zu überführen, wenn in ihr nicht auf Member (Prozeduren und Variablen) verwiesen wird.

    Das macht natürlich Sinn, denn dann kannst Du die Methode Solo aufrufen, ohne eine Instanz erstellen zu müssen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Okay also wenn ich das richtig verstehe ist die Ansicht hier: Besser static wenn keine Attribute der Klasse in der Methode benötigt werden und alternativ einfach beide Implementierungen.

    Danke :)

    Ich würde das so handhaben:

    Bei >= 3 Argumenten:
    Instanzmethoden geben dem ersten Argument optisch eine besondere Bedeutung. Nur wenn dieses erste Argument auch wirklich eine besondere Bedeutung hat, sollte man eine Instanzmethode verwenden. Wenn alle Argumente "gleichwertig" sind, dann sollte es eine statische Methode sein.
    Gut:

    VB.NET-Quellcode

    1. Dim Title = "Dr."
    2. Dim FirstName = "Niko"
    3. Dim LastName = "Ortner"
    4. Dim DisplayName = String.Concat(Title, " ", FirstName, " ", LastName) '"Dr. Niko Ortner"
    5. Dim Message = "Hallo Welt"
    6. MessageBox.Show("{0} sagt {1}.".Format(DisplayName, Message))

    Nicht so gut:

    VB.NET-Quellcode

    1. Dim Title = "Dr."
    2. Dim FirstName = "Niko"
    3. Dim LastName = "Ortner"
    4. Dim DisplayName = Title.Concat(" ", FirstName, " ", LastName) '"Dr. Niko Ortner"
    5. Dim Message = "Hallo Welt"
    6. MessageBox.Show(String.Format("{0} sagt {1}.", DisplayName, Message))
    (String.Format ist übrigens genau "falsch" herum. Warum das so gemacht wurde weiß ich nicht.)

    Bei 2 Argumenten:
    Hier gilt das gleiche wie bei >= 3 Argumenten, aber es gibt öfter Ausnahmen.
    Gut:

    VB.NET-Quellcode

    1. Dim VertexNormal As New Vector3D(0, 0, 1)
    2. Dim VertexTangent As New Vector3D(0, 1, 0)
    3. Dim VertexBiTangent = Vector3D.CrossProduct(VertexNormal, VertexTangent)

    Nicht so gut:

    VB.NET-Quellcode

    1. Dim VertexNormal As New Vector3D(0, 0, 1)
    2. Dim VertexTangent As New Vector3D(0, 1, 0)
    3. Dim VertexBiTangent = VertexNormal.CrossProduct(VertexTangent)

    Aber folgendes ist manchmal auch nötig:

    VB.NET-Quellcode

    1. Dim Something As Object = ...
    2. If Something.Equals(New Vector3D(0, 0, 0)) Then
    3. End If
    4. 'Equals (die Instanzmethode) ist im Object-Typ deklariert und Sinn der Sache ist es, dass man diese überschreibt, sodass man Objekte auch vergleichen kann, wenn man zur Compilezeit den genauen Typ nicht kennt.
    5. 'Die überschreibende Equals-Funktion ist dann in Vector3D deklariert, natürlich ebenfalls als Instanzmethode:
    6. Structure Vector3D
    7. Public Overrides Function Equals(Other As Object) As Boolean
    8. If Not TypeOf Other Is Vector3D Then Return False
    9. 'etc.
    10. End Function
    11. End Structure


    Bei einem Argument:
    Statische Methoden bei denen das einzige Argument von dem Typ ist, in dem die Methode deklariert ist, sind nur seeehr selten wirklich sinnvoll. Im Framework gibt es interessanterweise viele solche Methoden:

    VB.NET-Quellcode

    1. Char.IsDigit, Char.IsLetter, etc.
    2. String.IsWhiteSpace
    3. Double.IsNaN
    4. Array.Sort
    5. Rectangle.Inflate

    Warum das so gelöst wurde, weiß ich nicht, aber wenn man nur den Stil betrachtet, würde ich das nicht so machen.
    Gut:

    VB.NET-Quellcode

    1. If TextBox_FilePath.Text.IsNullOrWhiteSpace() Then

    Nicht so gut:

    VB.NET-Quellcode

    1. If String.IsNullOrWhiteSpace(TextBox_FilePath.Text) Then



    Das sind natürlich nur Guidelines und keine festen Regeln. Guidelines kann man auch mal umgehen, wenn es in der Situation sinnvoll ist. Ich denke, das ist auch irgendwo Gefühlssache und Erfahrung.
    Hier ein Beispiel für so eine Ausnahme: Verwendet man das Argument nur als ganzes und "schaut nie hinein", dann kann es sinnvoll sein, eine statische Methode zu verwenden.
    Also zum Beispiel das:

    VB.NET-Quellcode

    1. Dim Chair As New ChairEntity
    2. Entity.Register(Chair)
    3. Class Entity
    4. Private Shared InstanceIDs As New Dictionary(Of Entity, Integer)
    5. Private Shared InstanceCounter As Integer = 0
    6. Public Shared Sub Register(Instance As Entity)
    7. InstanceIDs.Add(Instance, InstanceCounter)
    8. InstanceCounter += 1
    9. End Sub
    10. End Class
    11. Class ChairEntity
    12. Inherits Entity
    13. End Class

    Anstatt das:

    VB.NET-Quellcode

    1. Dim Chair As New ChairEntity
    2. Chair.Register()
    3. Class Entity
    4. Private Shared InstanceIDs As New Dictionary(Of Entity, Integer)
    5. Private Shared InstanceCounter As Integer = 0
    6. Public Sub Register()
    7. InstanceIDs.Add(Me, InstanceCounter)
    8. InstanceCounter += 1
    9. End Sub
    10. End Class
    11. Class ChairEntity
    12. Inherits Entity
    13. End Class

    Ich kann das jetzt nicht rational begründen, aber ich hatte mal so eine Situation (den Entities IDs zu geben hat das Debuggen extrem erleichtert) und da empfand ich es als schöner, eine statische Methode zu verwenden.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    TheVBTutorialsVB schrieb:

    Okay also wenn ich das richtig verstehe ist die Ansicht hier: Besser static wenn keine Attribute der Klasse in der Methode benötigt werden und alternativ einfach beide Implementierungen.

    Danke :)
    Dem würde ich widersprechen. Sowohl inhaltlich als auch dass icch denke, dass du das nicht richtig verstanden hast.
    Rod gibt dir doch gute Guideline - ist das nicht genau, wonach du frugst?

    RodFromGermany schrieb:

    kommt immer dann der Hinweis, eine Prozedur nach static zu überführen, wenn in ihr nicht auf Member (Prozeduren und Variablen) verwiesen wird.
    Wenn du nun eine Methode

    TheVBTutorialsVB schrieb:

    public bool IsOrthogonal(Vector3 other)...
    anlegst, dann greift die doch notwendigerweise auf Objekt-Member zu, und damit ists doch geklärt.



    Aber auch ohne iwelche herangezogene GuideLines - denk doch einfach mal einfach:
    Man sagt: "Vektor A ist Orthogonal zu Vektor B".
    Designe so, dass das direkt in Code formulierbar ist:

    VB.NET-Quellcode

    1. dim isOrth As Boolean = A.IsOrthogonal(B)
    Intuitiver kann man das meiner Meinung nach nicht designen - was meinst du?
    @ErfinderDesRades Moin.
    Wenn ich mir die static Procedures in den Klassen Rectangle und Co ansehe, ware dies ebenfalls salonfähig:

    VB.NET-Quellcode

    1. dim isOrth As Boolean = IsOrthogonal(A, B)
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hi
    ich kenne keine abstrakte Regel, würde aber am ehesten auf Niko Ortners Versuch, die Intuition dahinter zu erklären, zurückgreifen.

    Bzgl. String.Format: Ich vermute, dass es damit zusammenhängt, dass man "{0} ist so kluk.".Format("~blaze~") nicht lesen kann und den Zusammenhang bei komplexeren Zusammenhängen nicht erkennen kann. Da ist mir String.Format("{0} verwendet _niemals_ Ironie.", "~blaze~") dann doch lieber.

    Mit der Zeit bildet (bzw. zumindest hat es das bei mir getan) sich eine gewisse Intuition im Umgang mit den statischen Methoden aus. Bei mir ist die Intuition eher, dass statische Methoden etwas von außen betrachten, wohingegen die Instanz bei einem Aufruf auf ihr von sich aus etwas mitteilt.
    Bspw.: char.IsDigit betrachtet von außen, ob der angegebene char eine Zahl 0-9 ist. Würde man es auf char selbst implementieren (wovon ich allerdings nicht weiß, ob das ohne weiteres möglich ist, da es sich um primitive Datentypen handelt), würde man eher die Instanz dazu auffordern, ihren Status mitzuteilen.
    Ich denke, man befindet sich irgendwo auf dem Kontinuum zwischen "Gehört zur Instanz" und "Beurteilt die Instanz von außen" und trifft die Entscheidung dann auf dieser Basis, aber sicher wäre ich mir jetzt nicht.

    Edit:
    @Niko Ortner: Bzgl. deines Codes nochmal eine Anmerkung: Wenn du TryCast verwendest (mit Nullable geht das auch für Strukturen), kannst du dir das TypeOf sparen. Beachte aber, dass die Überprüfung a.GetType() == b.GetType() exakter ist und in den meisten Fällen die "Wahrheit" liefert.

    Viele Grüße
    ~blaze~
    Von mir dann ergänzend auch noch eine Anmerkung, weil das bisher nicht bemerkt wurde:

    Niko Ortner schrieb:

    If TextBox_FilePath.Text.IsNullOrWhiteSpace() Then

    erzeugt dummerweise eine NullReferenceException, wenn der Text tatsächlich null sein sollte. Dürfte zwar bei Textboxen quasi nicht vorkommen (ich weiß nichtmal, ob das bei Textboxen überhaupt geht), aber auf Strings allgemein bezogen bevorzuge ich deswegen dann doch lieber die statische Methode String.IsNullOrWhitespace() bzw. String.IsNullOrEmpty().
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.

    ~blaze~ schrieb:

    Bzgl. String.Format: Ich vermute, dass es damit zusammenhängt, dass man "{0} ist so kluk.".Format("~blaze~") nicht lesen kann und den Zusammenhang bei komplexeren Zusammenhängen nicht erkennen kann. Da ist mir String.Format("{0} verwendet _niemals_ Ironie.", "~blaze~") dann doch lieber.

    Darüber hatte ich auch nachgedacht. "{0} grüßt {1}".Format("Niko", "~blaze~") sieht im ersten Moment tatsächlich sehr ungewöhnlich aus. Aber ich kann nicht beurteilen, ob mir das nur so komisch vor kommt, weil ich es von String.Format anders gewohnt bin, oder ob ich es tatsächlich einfach so nicht schön finde.
    Ich stimme Dir jedenfalls zu, dass String.Format am Anfang einer langen Zeile besser zu erkennen ist, als erst hinter dem Format-String.


    ~blaze~ schrieb:

    Wenn du TryCast verwendest (mit Nullable geht das auch für Strukturen), kannst du dir das TypeOf sparen

    Geht übrigens nur in C#. Also C# 2010 kann das bereits, VB 2015 kann es nicht (eventuell im 2017-er schon?). Ich dachte erst, der C#-Compiler hätte hier einen Spezialfall und würde effektiv das kompilieren:

    C#-Quellcode

    1. Nullable<int> AsInt = null;
    2. if (Other is int)
    3. {
    4. AsInt = new Nullable<int>((int)Other);
    5. }

    aber Nullable<int> AsInt = Other as int?; wird tatsächlich direkt so kompiliert:

    Quellcode

    1. IL_0000: ldarg.1
    2. IL_0001: isinst valuetype [mscorlib]System.Nullable`1<int32>
    3. IL_0006: unbox.any valuetype [mscorlib]System.Nullable`1<int32>
    4. IL_000b: stloc.0

    Das ist also ein Spezialfall in der CLR, denn normalerweise resultiert isinst in false, wenn der Typ nicht exakt übereinstimmt (oder davon erbt) und unbox kann normalerweise nur das rausholen, was tatsächlich drin ist.
    Interessant, dass es das in VB noch nicht gibt.


    ~blaze~ schrieb:

    a.GetType() == b.GetType() exakter ist und in den meisten Fällen die "Wahrheit" liefert.

    Falls Du damit meinst, dass z.B. (New B).Equals(New D) auch True zurückgibt, weil D von B abgeleitet ist, dann ja. (Dieser Fall kann bei Wertetypen nicht eintreten.) Ich habe Equals aber ehrlich gesagt zu wenig verwendet, um abschätzen zu können, was sinnvoller ist. Nach Bauchgefühl würde ich sagen, dass das Verhalten Deiner Variante besser ist.
    Nebenbei: Hier muss man dann explizit auf Other auf Nothing prüfen, was man sich wiederum bei Typeof Other Is __ spart ;)


    Arby schrieb:

    erzeugt dummerweise eine NullReferenceException, wenn der Text tatsächlich null sein sollte.

    Weißt Du was? Das ist mir jetzt ehrlich gesagt schon ein bisschen peinlich, dass mir das nicht gleich aufgefallen ist. Ich schieb's mal auf meine Müdigkeit.
    In diesem Fall würde eine Instanzmethode natürlich überhaupt nicht funktionieren. Wobei ich aber sagen muss, solche Fälle sehe ich selten. Also eine Funktion, die ein Objekt der Klasse verwendet, in der sie deklariert ist, und dann sowohl für Nothing, als auch für manche Zustände konzeptuell das gleiche Ergebnis liefert. Bei Strings braucht man das recht oft, aber zum Beispiel gibt es folgendes nicht:

    VB.NET-Quellcode

    1. Public Class Process 'In System.Diagnostics
    2. Public Shared Function IsNullOrHasExited(p As Process) As Boolean
    3. Return p Is Nothing OrElse p.HasExited
    4. End Function
    5. End Class
    weil es nicht häufig der Fall ist, dass es sinnvoll ist, Nothing und einen gewissen Zustand als gleich zu betrachten.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Imho solltest du sie als Public Function von Vector machen. Da es ja mehr sinn macht einen Vektor mit einem anderen zu vergleichen. Und am ende willst du ja wissen, ob V2 zu deinem aktuellen Vektor rechtwinklig ist. Daher: machs als Member Function von Vektor

    @Niko: Wäre dann zu​VertexNormal.CrossProduct(VertexTangent) folgendes ​VertexNormal.MultiplyBy(VertexTangent) eine Alternative?
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell

    Radinator schrieb:

    eine Alternative

    Bedingt. Siehe unten. Aber allgemein gefällt es mir nicht, dass die beiden Vektoren nicht als "gleichwertig" erscheinen. VertexNormal ist ja nicht speziell. Aber die Namenskonvention, "By" hier anzuhängen, finde ich gut.

    ErfinderDesRades schrieb:

    Oder einen *-Operator bereitstellen

    Allgemein ja. Aber bei Vektoren muss man aufpassen. Es gibt DotMultiplication und CrossMultiplication. Verwendet man hier den *-Operator, dann erkenn man nicht gut, welches von beiden gemeint ist. Aber den *-Operator für Vektoren mit Skalaren zu verwenden, ist kein Problem.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    ~blaze~ schrieb:


    Bspw.: char.IsDigit betrachtet von außen, ob der angegebene char eine Zahl 0-9 ist. Würde man es auf char selbst implementieren (wovon ich allerdings nicht weiß, ob das ohne weiteres möglich ist, da es sich um primitive Datentypen handelt)


    Natürlich hätte man Char.isDigit auch als Instanzmethode implementieren können. char ist ja auch nur ein struct. Die Idee dahinter, dass die Methode statisch ist, ist diejenige, dass sie threadsicher ist. Die meisten Methoden in der Framework Class Library folgen diesem Schema (statische Methode = threadsicher, Instanzmethode = nicht threadsicher)

    Siehe auch hier
    jonskeet.uk/csharp/threads/threadsafe.shtml
    sd.blackball.lv/library/CLR_vi…_Richter_4th_Edition).pdf S.751

    ErfinderDesRades schrieb:

    Oder einen *-Operator bereitstellen?

    in der Mathematik wird das Kreuzprodukt von Vektoren immer mit x dargestellt, also würde ich dafür niemals den * Operator bereitstellen, dann noch eher das Skalarprodukt, aber auch da finde ich es meiner Meinung nach nicht angebracht.
    XNA macht hier z.B. ein Komponentenweises multiplizieren:
    msdn.microsoft.com/en-us/library/bb198137.aspx

    Meine Meinung aus Sicht der Grafikentwicklung:
    Beides bereitstellen manchmal ist das eine übersichtlicher, manchmal das andere.
    Mit tendenz zu den static Methoden für die meisten sachen^^
    Performancetechnisch macht es am ende ebenfalls keinen Unterschied, vorallem sollte man mMn Vectoroperationen immer schön inlinen.
    Kannst dich ja auch einfach hier inspirieren lassen:
    msdn.microsoft.com/en-us/libra…work.vector3_members.aspx

    Wenn sich jemand beschwert kannst immer noch sagen du hältsts dich halt so gut an die Regeln wie Microsoft selbst, das muss ja reichen^^
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---