Ab wann ist Kürze keine Tugend mehr?

102

Nach einer kürzlichen Fehlerbehebung musste ich den von anderen Teammitgliedern geschriebenen Code durchgehen, wobei ich Folgendes fand (es ist C #):

return (decimal)CostIn > 0 && CostOut > 0 ? (((decimal)CostOut - (decimal)CostIn) / (decimal)CostOut) * 100 : 0;

Wenn man bedenkt, dass es für all diese Darsteller einen guten Grund gibt, scheint es immer noch sehr schwierig zu sein, dem zu folgen. Es gab einen kleinen Fehler in der Berechnung und ich musste ihn entwirren, um das Problem zu beheben.

Ich kenne den Codierungsstil dieser Person aus der Codeüberprüfung, und sein Ansatz ist, dass kürzer fast immer besser ist. Und da gibt es natürlich einen Wert: Wir haben alle unnötig komplexe Ketten bedingter Logik gesehen, die mit ein paar gut platzierten Operatoren aufgeräumt werden könnten. Aber er ist eindeutig geschickter als ich, wenn es darum geht, Bedienerketten zu einer einzigen Aussage zusammenzufassen.

Dies ist natürlich letztendlich eine Frage des Stils. Wurde jedoch irgendetwas geschrieben oder erforscht, um den Punkt zu erkennen, an dem das Streben nach Kürze des Codes nicht mehr nützlich ist und zum Hindernis für das Verständnis wird?

Der Grund für die Besetzungen ist Entity Framework. Die Datenbank muss diese als nullfähige Typen speichern. Dezimal? ist nicht gleichbedeutend mit Decimal in C # und muss umgewandelt werden.

Bob Tway
quelle
153
Wenn die Kürze die Lesbarkeit übertrifft.
Robert Harvey
27
Betrachten Sie Ihr spezielles Beispiel: Eine Besetzung ist entweder (1) ein Ort, an dem der Entwickler mehr weiß als der Compiler und dem Compiler eine Tatsache mitteilen muss, die nicht abgeleitet werden kann, oder (2) wo einige Daten im "falschen" Verzeichnis gespeichert werden "Typ für die Arten von Operationen, die wir darauf ausführen müssen. Beide sind starke Indikatoren dafür, dass etwas überarbeitet werden könnte. Die beste Lösung besteht darin, einen Weg zu finden, um den Code ohne Casts zu schreiben.
Eric Lippert
29
Insbesondere scheint es bizarr, dass es notwendig ist, CostIn auf Dezimal zu setzen, um es mit Null zu vergleichen, nicht jedoch CostOut. warum ist das so? Was auf der Erde ist der Typ von CostIn, der nur durch Umwandlung in eine Dezimalzahl mit Null verglichen werden kann? Und warum ist CostOut nicht vom selben Typ wie CostIn?
Eric Lippert
12
Darüber hinaus kann die Logik hier tatsächlich falsch sein. Angenommen, CostOutist gleich Double.Epsilonund ist daher größer als Null. Ist (decimal)CostOutaber in diesem Fall Null, und wir haben einen Division durch Null-Fehler. Der erste Schritt sollte darin bestehen, den Code richtig zu machen , was meiner Meinung nach nicht der Fall ist. Machen Sie es richtig, machen Sie Testfälle und machen Sie es dann elegant . Eleganter Code und kurzer Code haben vieles gemeinsam, aber manchmal ist Kürze nicht die Seele der Eleganz.
Eric Lippert
5
Kürze ist immer eine Tugend. Unsere objektive Funktion kombiniert Kürze mit anderen Tugenden. Wenn man kürzer sein kann, ohne andere Tugenden zu verletzen, sollte man immer.
Solomonoffs Geheimnis

Antworten:

163

Zur Beantwortung Ihrer Frage zur vorhandenen Forschung

Wurde jedoch irgendetwas geschrieben oder erforscht, um den Punkt zu erkennen, an dem das Streben nach Kürze des Codes nicht mehr nützlich ist und zum Hindernis für das Verständnis wird?

Ja, in diesem Bereich wurde gearbeitet.

Um ein Verständnis für diese Dinge zu erlangen, müssen Sie einen Weg finden, eine Metrik zu berechnen, damit Vergleiche auf quantitativer Basis durchgeführt werden können (anstatt den Vergleich nur auf der Grundlage von Witz und Intuition durchzuführen, wie es die anderen Antworten tun). Eine mögliche Metrik, die untersucht wurde, ist

Zyklomatische Komplexität ÷ Quellcodezeilen ( SLOC )

In Ihrem Codebeispiel ist dieses Verhältnis sehr hoch, da alles auf eine Zeile komprimiert wurde.

Die SATC hat festgestellt, dass die effektivste Bewertung eine Kombination aus Größe und [zyklomatischer] Komplexität ist. Die Module mit sowohl einer hohen Komplexität als auch einer großen Größe neigen dazu, die geringste Zuverlässigkeit zu haben. Module mit geringer Größe und hoher Komplexität stellen auch ein Zuverlässigkeitsrisiko dar, da es sich bei ihnen in der Regel um sehr knappen Code handelt, der schwer zu ändern oder zu modifizieren ist.

Verknüpfung

Hier sind einige Referenzen, wenn Sie interessiert sind:

McCabe, T. und A. Watson (1994), Software-Komplexität (CrossTalk: The Journal of Defense Software Engineering).

Watson, AH & McCabe, TJ (1996). Strukturiertes Testen: Eine Testmethode unter Verwendung der Cyclomatic Complexity Metric (NIST Special Publication 500-235). Abgerufen am 14. Mai 2011 von der McCabe Software-Website: http://www.mccabe.com/pdf/mccabe-nist235r.pdf

Rosenberg, L., Hammer, T., Shaw, J. (1998). Software-Metriken und Zuverlässigkeit (Fortführung des IEEE International Symposium on Software Reliability Engineering). Abgerufen am 14. Mai 2011 von der Website der Penn State University: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.104.4041&rep=rep1&type=pdf

Meine Meinung und Lösung

Persönlich habe ich nie Kürze, nur Lesbarkeit geschätzt. Manchmal hilft Kürze die Lesbarkeit, manchmal nicht. Wichtiger ist, dass Sie Really Obvious Code (ROC) anstelle von Write-Only Code (WOC) schreiben.

Nur zum Spaß, so würde ich es schreiben und Mitglieder meines Teams bitten, es zu schreiben:

if ((costIn <= 0) || (costOut <= 0)) return 0;
decimal changeAmount = costOut - costIn;
decimal changePercent = changeAmount / costOut * 100;
return changePercent;

Beachten Sie auch, dass die Einführung der Arbeitsvariablen den glücklichen Nebeneffekt hat, Festkomma-Arithmetik anstelle von Ganzzahl-Arithmetik auszulösen, so dass die Notwendigkeit für alle diese Casts decimalbeseitigt ist.

John Wu
quelle
4
Ich mag die Schutzklausel für den Fall kleiner als Null sehr. Könnte eine Bemerkung wert sein: Wie können die Kosten unter Null liegen, welcher Sonderfall ist das?
user949300
22
+1. Ich würde wahrscheinlich etwas hinzufügen wieif ((costIn < 0) || (costOut < 0)) throw new Exception("costs must not be negative");
Doc Brown
13
@ DocBrown: Das ist ein guter Vorschlag, aber ich würde überlegen, ob der außergewöhnliche Codepfad durch einen Test ausgeübt werden kann. Wenn ja, schreiben Sie einen Testfall, der diesen Codepfad ausübt. Wenn nein, ändern Sie das Ganze in einen Assert.
Eric Lippert
12
Obwohl dies alles gute Punkte sind, glaube ich, dass der Umfang dieser Frage der Codestil und nicht die Logik ist. Mein Code-Snip ist eine Bemühung um funktionale Äquivalenz aus der Black-Box-Perspektive. Das Auslösen einer Ausnahme ist nicht dasselbe wie das Zurückgeben von 0, sodass diese Lösung nicht funktional äquivalent wäre.
John Wu
30
"Persönlich habe ich nie die Kürze, sondern nur die Lesbarkeit geschätzt. Manchmal hilft die Kürze der Lesbarkeit, manchmal nicht." Hervorragender Punkt. +1 nur dafür. Ich mag auch Ihre Code-Lösung.
Wildcard
49

Die Kürze ist gut, wenn das Durcheinander der wichtigen Dinge verringert wird. Wenn es jedoch knapp wird und zu viele relevante Daten zu dicht gepackt werden , um sie ohne Weiteres zu verfolgen, werden die relevanten Daten selbst zu einem Durcheinander, und Sie haben ein Problem.

In diesem speziellen Fall werden die Abdrücke decimalwiederholt. es wäre wahrscheinlich insgesamt besser, es so umzuschreiben:

var decIn = (decimal)CostIn;
var decOut = (decimal)CostOut;
return decIn > 0 && CostOut > 0 ? (decOut - decIn ) / decOut * 100 : 0;
//                  ^ as in the question

Plötzlich ist die Zeile mit der Logik viel kürzer und passt auf eine horizontale Zeile, sodass Sie alles sehen können, ohne scrollen zu müssen, und die Bedeutung ist viel deutlicher.

Mason Wheeler
quelle
1
Ich wäre wahrscheinlich weiter gegangen und hätte eine ((decOut - decIn ) / decOut) * 100andere Variable überarbeitet.
FrustratedWithFormsDesigner
9
Assembler war viel klarer: nur eine Operation pro Zeile. Doh!
2
@FrustratedWithFormsDesigner Ich würde sogar noch einen Schritt weiter gehen und die bedingte Prüfung in Klammern setzen.
Chris Cirefice
1
@FrustratedWithFormsDesigner: Wenn Sie dies vor der Bedingung extrahieren, wird die Prüfung durch Nullteilung ( CostOut > 0) umgangen , sodass Sie die Bedingung in eine if-Anweisung umwandeln müssen. Nicht, dass daran etwas falsch ist, aber es fügt mehr Ausführlichkeit hinzu als nur die Einführung eines Einheimischen.
Wchargin
1
Um ehrlich zu sein, nur die Casts loszuwerden, scheint so, als hätten Sie IMO genug ausgestrahlt. Wenn es jemand schwer findet zu lesen, weil er eine einfache Grundratenberechnung nicht erkennt, ist das sein Problem, nicht meins.
Walfrat
7

Ich kann zwar keine bestimmten Untersuchungen zu diesem Thema anführen, aber ich würde vorschlagen, dass all diese Darstellungen in Ihrem Code gegen das Prinzip "Don't Repeat Yourself" verstoßen. Was ist Ihr Code erscheint das zu tun , um zu versuchen , ist zu konvertieren costInund costOutzu geben Decimal, und dann einige Integritätsprüfungen auf den Ergebnissen solcher Umwandlungen durchführen, und die Durchführung von zusätzlichen Operationen auf diesen gewandelten Werten , wenn die Kontrollen passieren. Tatsächlich führt Ihr Code eine der Sicherheitsüberprüfungen für einen nicht konvertierten Wert durch, wodurch sich die Möglichkeit ergibt, dass costOut einen Wert enthält, der größer als Null ist, jedoch weniger als halb so groß wie der kleinste Wert ungleich Null, der dargestellt werden Decimalkann. Der Code wäre viel klarer, wenn er Variablen vom Typ definieren würde Decimal, die die konvertierten Werte enthalten, und dann auf diese reagieren würde.

Es scheint merkwürdig, dass Sie mehr am Verhältnis der DecimalDarstellungen von costInund costOutals am Verhältnis der tatsächlichen Werte von costInund interessiert sind costOut, es sei denn, der Code wird die Dezimaldarstellungen auch für einen anderen Zweck verwenden. Wenn der Code diese Repräsentationen weiter nutzt, wäre dies ein weiteres Argument für die Erstellung von Variablen, die diese Repräsentationen enthalten, anstatt eine fortlaufende Folge von Casts im gesamten Code zu haben.

Superkatze
quelle
Die (Dezimal-) Casts erfüllen wahrscheinlich einige Anforderungen an Geschäftsregeln. Im Umgang mit Buchhaltern muss man manchmal durch blöde Reifen springen. Ich dachte, dass der CFO einen Herzinfarkt erleiden würde, als er die Zeile "Steuerrundungskorrektur verwenden - 0,01 USD" fand, die angesichts der angeforderten Funktionalität unvermeidbar war. (Geliefert: Preis nach Steuern. Ich muss den Preis vor Steuern berechnen. Die Wahrscheinlichkeit, dass keine Antwort
eingeht,
@LorenPechtel: Angesichts der Tatsache, dass die Genauigkeit einer DecimalBesetzung von der Größe des betreffenden Werts abhängt, kann ich mir kaum Geschäftsregeln vorstellen, die das tatsächliche Verhalten der Besetzungen vorgeben .
Superkatze
1
Guter Punkt - ich hatte die Details des Dezimaltyps vergessen, weil ich nie die Gelegenheit hatte, so etwas zu wollen. Ich denke jetzt, das ist der Befolgung der Geschäftsregeln durch den Frachtkult - insbesondere darf Geld kein Fließkomma sein.
Loren Pechtel
1
@LorenPechtel: Um meinen letzten Punkt zu verdeutlichen: Ein Festkommatyp kann garantieren, dass x + yy entweder überläuft oder y ergibt. Der DecimalTyp tut es nicht. Der Wert 1.0d / 3.0 enthält mehr Stellen rechts von der Dezimalstelle, als bei Verwendung größerer Zahlen beibehalten werden können. Wenn Sie also dieselbe größere Zahl addieren und dann subtrahieren, geht die Genauigkeit verloren. Bei Festkommatypen kann die Genauigkeit durch gebrochene Multiplikation oder Division verloren gehen, jedoch nicht durch Addition, Subtraktion, Multiplikation oder Division mit Rest (z. B. 1,00 / 7 ist 0,14 Rest 0,2; 1,00 Div. 0,15 ist 6 Rest 0,10).
Superkatze
1
@Hulk: Ja natürlich. Ich überlegte, ob ich x + yy als Ergebnis von x, x + yx als Ergebnis von y oder x + yxy verwenden sollte, und endete damit, die ersten beiden durcheinander zu bringen. Wichtig ist, dass Festkommatypen garantieren können, dass viele Abfolgen von Operationen niemals unerkannte Rundungsfehler beliebiger Größe verursachen. Wenn Code Dinge auf unterschiedliche Weise zusammenfügt, um zu überprüfen, ob die Summen übereinstimmen (z. B. Vergleichen der Summe der Zeilensummen mit der Summe der Spaltensummen), ist es viel besser, wenn die Ergebnisse exakt gleich sind, als wenn sie "nah" herauskommen.
Superkatze
5

Ich schaue auf diesen Code und frage: "Wie können Kosten 0 (oder weniger) sein?". Welchen Sonderfall deutet das an? Code sollte sein

bool BothCostsAreValidProducts = (CostIn > 0) && (CostOut > 0);
if (!BothCostsAreValidProducts)
  return NO_PROFIT;
else {
   // that calculation here...
}

Ich vermute, was die Namen hier angeht: ändern BothCostsAreValidProductsund NO_PROFITentsprechend.

user949300
quelle
Natürlich können die Kosten Null sein (denken Sie an Weihnachtsgeschenke). Und in Zeiten negativer Zinsen sollten negative Kosten auch nicht allzu überraschend sein, und zumindest sollte der Kodex darauf vorbereitet sein, damit umzugehen (und Fehler zu werfen)
Hagen von Eitzen
Sie sind auf dem richtigen Weg.
danny117
Das ist dumm. if (CostIn <= 0 || CostOut <= 0)ist völlig in Ordnung.
Miles Rout
Viel weniger lesbar. Der Variablenname ist schrecklich (BothCostsAreValid wäre besser. Es gibt hier nichts über Produkte). Aber auch das trägt nichts zur Lesbarkeit bei, denn nur das Überprüfen von CostIn und CostOut ist in Ordnung. Sie würden eine zusätzliche Variable mit einem aussagekräftigen Namen einführen, wenn die Bedeutung des zu testenden Ausdrucks nicht offensichtlich ist.
gnasher729
5

Kürze hört auf, eine Tugend zu sein, wenn wir vergessen, dass sie eher Mittel zum Zweck als eine Tugend an sich ist. Wir mögen Kürze, weil sie mit Einfachheit korreliert, und wir mögen Einfachheit, weil einfacher Code einfacher zu verstehen und zu ändern ist und weniger Fehler enthält. Am Ende möchten wir, dass der Code dieses Ziel erreicht:

  1. Erfüllen Sie die Geschäftsanforderungen mit dem geringsten Arbeitsaufwand

  2. Vermeiden Sie Fehler

  3. Lassen Sie uns in Zukunft Änderungen vornehmen, die weiterhin 1 und 2 erfüllen

Das sind die Ziele. Jedes Konstruktionsprinzip oder jede Konstruktionsmethode (KISS, YAGNI, TDD, SOLID, Proofs, Typensysteme, dynamische Metaprogrammierung usw.) ist nur insoweit tugendhaft, als sie uns dabei helfen, diese Ziele zu erreichen.

Die fragliche Linie scheint das Endziel aus den Augen verloren zu haben. Es ist zwar kurz, aber nicht einfach. Es enthält tatsächlich unnötige Redundanz, indem der gleiche Casting-Vorgang mehrmals wiederholt wird. Das Wiederholen von Code erhöht die Komplexität und die Wahrscheinlichkeit von Fehlern. Das Vermischen des Castings mit der tatsächlichen Berechnung macht es auch schwierig, dem Code zu folgen.

Die Linie hat drei Anliegen: Schutzeinrichtungen (Sondergehäuse 0), Typenguss und Berechnung. Jedes Anliegen ist ziemlich einfach, wenn man es isoliert betrachtet, aber da es alle in den gleichen Ausdruck gemischt hat, wird es schwierig, ihm zu folgen.

Es ist nicht klar, warum CostOutnicht das erste Mal gegossen wird, wenn es verwendet CostInwird. Es mag einen guten Grund geben, aber die Absicht ist nicht klar (zumindest nicht ohne Kontext), was bedeutet, dass ein Betreuer vorsichtig wäre, diesen Code zu ändern, weil es möglicherweise einige versteckte Annahmen gibt. Und das ist ein Gräuel für die Wartbarkeit.

Da CostInvor dem Vergleich mit 0 gegossen wird, gehe ich davon aus, dass es sich um einen Gleitkommawert handelt. (Wenn es ein Int wäre, gäbe es keinen Grund zum Besetzen). Wenn CostOutes sich jedoch um einen Gleitkomma handelt, kann der Code einen obskuren Fehler bei der Division durch Null verbergen, da ein Gleitkommawert zwar klein, aber ungleich Null ist, aber in eine Dezimalzahl umgewandelt null (zumindest glaube ich, dass dies möglich ist).

Das Problem ist also nicht die Kürze oder das Fehlen von Kürze, sondern die wiederholte Logik und das Zusammentreffen von Bedenken, die zu schwer zu wartendem Code führen.

Das Einführen von Variablen zur Speicherung der umgesetzten Werte würde wahrscheinlich die Größe des Codes erhöhen, der in der Anzahl der Token gezählt wird, aber die Komplexität verringern, die Bedenken trennen und die Übersichtlichkeit verbessern, was uns näher an das Ziel des Codes bringt, der einfacher zu verstehen und zu verwalten ist.

JacquesB
quelle
1
Ein wichtiger Punkt: Das einmalige und nicht das zweimalige Casting von CostIn macht es unlesbar, da der Leser keine Ahnung hat, ob es sich um einen subtilen Fehler mit einer offensichtlichen Korrektur handelt oder ob dies absichtlich geschieht. Klar, wenn ich nicht genau sagen kann, was der Schreiber mit einem Satz gemeint hat, ist er nicht lesbar. Es sollten entweder zwei Besetzungen vorhanden sein oder ein Kommentar, der erklärt, warum die erste Verwendung von CostIn keine Besetzung erfordert oder haben sollte.
gnasher729
3

Kürze ist überhaupt keine Tugend. Lesbarkeit ist DIE Tugend.

Kürze kann ein Werkzeug sein, um die Tugend zu erreichen, oder, wie in Ihrem Beispiel, ein Werkzeug, um genau das Gegenteil zu erreichen. Auf die eine oder andere Weise hat es fast keinen eigenen Wert. Die Regel, dass Code "so kurz wie möglich" sein sollte, kann ebenso gut durch "so obszön wie möglich" ersetzt werden - sie sind alle gleich bedeutungslos und können potenziell schädlich sein, wenn sie keinem größeren Zweck dienen.

Außerdem folgt der von Ihnen veröffentlichte Code nicht einmal der Kürze. Wären die Konstanten mit M Suffix deklariert werden, die meisten der schrecklichen (decimal)Abgüsse werden könnten vermieden werden, da der Compiler fördern würde noch intzu decimal. Ich glaube, dass die Person, die Sie beschreiben, nur die Kürze als Ausrede verwendet. Höchstwahrscheinlich nicht absichtlich, aber dennoch.

Agent_L
quelle
2

In meiner jahrelangen Erfahrung bin ich zu der Überzeugung gelangt , dass die ultimative Kürze die Zeit ist - die Zeit dominiert alles andere. Dies umfasst sowohl die Zeit für die Leistung - wie lange ein Programm für die Ausführung eines Auftrags benötigt - als auch die Zeit für die Wartung - wie lange es dauert, Features hinzuzufügen oder Fehler zu beheben. (Wie Sie diese beiden Faktoren ausbalancieren, hängt davon ab, wie oft der betreffende Code ausgeführt oder verbessert wird. Denken Sie daran, dass die vorzeitige Optimierung immer noch die Wurzel allen Übels ist .) Die Kürze des Codes soll die Kürze beider verbessern. kürzerer Code wird normalerweise schneller ausgeführt und ist in der Regel einfacher zu verstehen und zu warten. Wenn dies auch nicht der Fall ist, handelt es sich um ein Nettonegativ.

In dem hier gezeigten Fall wurde meiner Meinung nach die Kürze des Texts auf Kosten der Lesbarkeit als Kürze der Zeilenzahl interpretiert, was die Wartungszeit verlängern kann. (Je nach Ausführung des Castings kann die Ausführung auch länger dauern. Wenn die obige Zeile jedoch nicht millionenfach ausgeführt wird, ist dies wahrscheinlich kein Problem.) In diesem Fall beeinträchtigen die wiederholten Dezimal-Casts die Lesbarkeit, da dies schwieriger ist Sehen Sie, was die wichtigste Berechnung ist. Ich hätte folgendes geschrieben:

decimal dIn = (decimal)CostIn;
decimal dOut = (decimal)CostOut;
return dIn > 0 && CostOut > 0 ? ((dOut - dIn) / dOut) * 100 : 0;

(Bearbeiten: Dies ist derselbe Code wie bei der anderen Antwort, also los geht's.)

Ich bin ein Fan des ternären Betreibers ? :, also würde ich das belassen.

Paul Brinkley
quelle
5
Ternaries sind schwer zu lesen, insbesondere wenn die Rückgabewerte Ausdrücke enthalten, die über einen einzelnen Wert oder eine Variable hinausgehen.
Almo
Ich frage mich, ob das die negativen Stimmen treibt. Mit Ausnahme dessen, was ich geschrieben habe, ist Mason Wheeler mit derzeit 10 Stimmen sehr einverstanden. Er ließ auch den Ternär rein. Ich weiß nicht, warum so viele Leute ein Problem damit haben ? :- ich denke, das obige Beispiel ist ausreichend kompakt, insb. im Vergleich zu einem Wenn-Dann-Anderen.
Paul Brinkley
1
Wirklich nicht sicher. Ich habe dich nicht abgelehnt. Ich mag keine Ternaries, weil es nicht klar ist, was sich auf beiden Seiten des Ternaries befindet :. if-elseliest sich wie Englisch: kann nicht verfehlen, was es bedeutet.
Almo
FWIW Ich vermute, Sie bekommen Ablehnungen, weil dies eine sehr ähnliche Antwort ist wie bei Mason Wheeler, aber er hat die erste bekommen.
Bob Tway
1
Tod des ternären Betreibers !! (auch Tod vor Tabulatoren, Leerzeichen und jeglichem Einklammerungs- und Einrückungsmodell mit Ausnahme von Allman (in der Tat hat The Donald (tm) getwittert, dass dies die ersten drei Gesetze sein werden, die er am 20.
Januar
2

Wie fast alle obigen Antworten sollte die Lesbarkeit immer Ihr vorrangiges Ziel sein. Ich denke jedoch auch, dass die Formatierung ein effektiverer Weg sein kann, um dies über das Erstellen von Variablen und neuen Zeilen zu erreichen.

return ((decimal)CostIn > 0 && CostOut > 0) ?
       100 * ( (decimal)CostOut - (decimal)CostIn ) / (decimal)CostOut:
       0;

Ich stimme dem Argument der zyklomatischen Komplexität in den meisten Fällen sehr zu, aber Ihre Funktion scheint klein und einfach genug zu sein, um mit einem guten Testfall besser angesprochen zu werden. Aus Neugierde, warum das Bedürfnis nach Dezimalstellen?

Rucksackkodierer
quelle
4
Der Grund für die Besetzungen ist Entity Framework. Die Datenbank muss diese als nullfähige Typen speichern. Doppelt? entspricht nicht Double in C # und muss umgewandelt werden.
Bob Tway
2
@MattThrower Du meinst decimal, richtig? double! = decimal, da gibt es einen großen Unterschied.
Pinkfloydx33
1
@ pinkfloydx33 ja! Tippen auf einem Telefon mit nur einem halben Gehirn beschäftigt :)
Bob Tway
Ich muss meinen Schülern erklären, dass sich SQL-Datentypen merkwürdig von den in Programmiersprachen verwendeten Typen unterscheiden. Ich konnte ihnen jedoch nicht erklären, warum . "Ich weiß es nicht!" "Little Endian!"
3
Ich finde das überhaupt nicht lesbar.
Almo
1

Für mich sieht es so aus, als liege ein großes Problem mit der Lesbarkeit in der fehlenden Formatierung.

Ich würde es so schreiben:

return (decimal)CostIn > 0 && CostOut > 0 
            ? (((decimal)CostOut - (decimal)CostIn) / (decimal)CostOut) * 100 
            : 0;

Je nachdem, ob es sich bei dem Typ CostInund CostOutum einen Gleitkomma- oder einen ganzzahligen Typ handelt, sind einige der Darstellungen möglicherweise auch nicht erforderlich. Im Gegensatz zu floatund doublewird implizit für ganzzahlige Werte geworben decimal.

Felix Dombek
quelle
Es tut mir leid zu sehen, dass dies ohne Erklärung abgelehnt wurde, aber es scheint mir identisch mit der Antwort von Backpackcodes abzüglich einiger seiner Bemerkungen zu sein, also nehme ich an, dass es gerechtfertigt war.
PJTraill
@PJTraill Das muss ich verpasst haben, es ist ja fast identisch. Ich bevorzuge es jedoch sehr, die Operatoren auf den neuen Leitungen zu haben, weshalb ich meine Version stehen lassen werde.
Felix Dombek
Ich stimme den Betreibern zu, wie ich in einem Kommentar zur anderen Antwort bemerkte - ich hatte nicht bemerkt, dass Sie es getan haben, wie ich es vorgezogen habe.
PJTraill
0

Code kann in Eile geschrieben werden, aber der obige Code sollte in meiner Welt mit viel besseren Variablennamen geschrieben werden.

Und wenn ich den Code richtig gelesen habe, wird versucht, eine Bruttomarge-Berechnung durchzuführen.

var totalSales = CostOut;
var totalCost = CostIn;
var profit = (decimal)(CostOut - CostIn);
var grossMargin = 0m; //profit expressed as percentage of totalSales

if(profit > 0)
{
    grossMargin = profit/totalSales*100
}
Thomas Koelle
quelle
3
Sie haben die Division durch Null verloren, und es wird Null zurückgegeben.
danny117
1
und deshalb ist es schwierig, den Code eines anderen zu überarbeiten, der der Kürze halber maximiert ist, und warum es gut ist, zusätzliche Kommentare zu haben, um zu erklären, warum / wie die Dinge funktionieren
Rudolf Olah
0

Ich gehe davon aus, dass CostIn * CostOut ganze Zahlen
sind. So würde ich es schreiben.
M (Money) ist dezimal

return CostIn > 0 && CostOut > 0 ? 100M * (CostOut - CostIn) / CostOut : 0M;
Paparazzo
quelle
1
Ich
2
Ist das durch Null dividierte noch da?
danny117
@ danny117 Wenn Kürze die falsche Antwort liefert, dann ist es zu weit gegangen.
Paparazzo
Sie möchten die Frage nicht aktualisieren und aktivieren. 100M und 0M erzwingen die Dezimalstelle. Ich denke, dass (CostOut - CostIn) als Ganzzahl-Mathematik durchgeführt wird und dann die Differenz auf Dezimal umgewandelt wird.
Paparazzo
0

Code ist geschrieben, um von Menschen verstanden zu werden. Die Kürze in diesem Fall kostet nicht viel und erhöht die Belastung des Betreuers. Aus diesem Kürze sollten Sie unbedingt , dass erweitern entweder , indem sie den Code selbsterklärend (besser Variablennamen) oder das Hinzufügen von weiteren Kommentaren zu erklären , warum es so funktioniert.

Wenn Sie heute Code schreiben, um ein Problem zu lösen, kann dieser Code morgen ein Problem sein, wenn sich die Anforderungen ändern. Die Wartung muss immer berücksichtigt werden, und eine bessere Verständlichkeit des Codes ist unerlässlich.

Rudolf Olah
quelle
1
Hier kommen die Praktiken und Prinzipien des Software Engineerings ins Spiel. Nicht-funktionale Anforderungen
hanzolo
0

Kürze ist keine Tugend mehr, wenn

  • Es gibt eine Division ohne vorherige Überprüfung auf Null.
  • Es erfolgt keine Überprüfung auf Null.
  • Es findet keine Bereinigung statt.
  • TRY CATCH versus wirft die Nahrungskette auf, wo der Fehler behoben werden kann.
  • Es werden Annahmen über die Erledigungsreihenfolge von asynchronen Aufgaben getroffen
  • Aufgaben mit Verzögerung statt späterer Terminverschiebung
  • Unnötige E / A wird verwendet
  • Kein optimistisches Update
danny117
quelle
Wenn es nicht lange genug Antwort gibt.
1
Ok, das sollte ein Kommentar sein, keine Antwort. Aber er ist ein neuer Typ, also erkläre es wenigstens; nicht nur abstimmen & weglaufen! Willkommen an Bord, Danny. Ich habe eine Ablehnung abgesagt, mache aber beim nächsten Mal einen Kommentar :-)
Mawg
2
Ok, ich habe die Antwort um einige komplexere Dinge erweitert, die ich auf die harte und die einfache Art gelernt habe, kurzen Code zu schreiben.
danny117
Vielen Dank für die Begrüßung @Mawg. Ich möchte darauf hinweisen, dass die Überprüfung auf Null das Problem ist, das am häufigsten im kurzen Code auftritt.
danny117
Ich habe gerade über Android bearbeitet und es wurde nicht nach einer Beschreibung der Bearbeitung gefragt. Ich habe ein optimistisches Update hinzugefügt (Änderung erkennen und warnen)
danny117
0

Wenn dies die Validierungseinheitentests besteht, ist es in Ordnung, wenn eine neue Spezifikation hinzugefügt, ein neuer Test oder eine erweiterte Implementierung erforderlich ist und die Kürze des Codes "entwirrt" werden muss Problem würde entstehen.

Offensichtlich zeigt ein Fehler im Code, dass es ein weiteres Problem mit der Frage / Antwort gab, bei dem es sich um ein Versehen handelte. Die Tatsache, dass ein Fehler aufgetreten ist, gibt also Anlass zur Sorge.

Bei nichtfunktionalen Anforderungen wie "Lesbarkeit" von Code muss dieser vom Entwicklungsmanager definiert und vom Hauptentwickler verwaltet und von den Entwicklern respektiert werden, um eine ordnungsgemäße Implementierung sicherzustellen.

Versuchen Sie, eine standardisierte Implementierung von Code (Standards und Konventionen) sicherzustellen, um die "Lesbarkeit" und die einfache "Wartbarkeit" zu gewährleisten. Wenn diese Qualitätsattribute jedoch nicht definiert und erzwungen werden, erhalten Sie Code wie im obigen Beispiel.

Wenn Sie diese Art von Code nicht sehen möchten, versuchen Sie, das Team dazu zu bringen, sich auf Implementierungsstandards und Konventionen zu einigen, diese aufzuschreiben und zufällige Codeüberprüfungen oder Stichproben durchzuführen, um zu überprüfen, ob die Konvention eingehalten wird.

Hanzolo
quelle