PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Visual Basic 2008 problem beim runden


Geldmann3
2010-12-05, 19:29:53
Hallo, ich habe in Visual Basic ein kleines Spiel erstellt. Am Ende soll dem Spieler die erreichte Punktzahl mitgeteilt werden.
So
MsgBox("Du hast " + Punktzahl + " Punkte erreicht, versuche es noch einmal")
funktioniert es.

Jetzt wollte ich das Ergebnis noch gerundet (Ohne Nachkommastellen)ausgeben lassen. Das habe ich so versucht.
MsgBox("Du hast " + Math.Round(Punktzahl, 0) + " Punkte erreicht, versuche es noch einmal")
Visual Basic zeigt mir allerdings einen Fehler an.
Fehler 1 Fehler bei der Überladungsauflösung, da keine zugreifbare "Round" ohne einschränkende Konvertierung aufgerufen werden kann:
'Public Shared Function Round(d As Decimal, mode As System.MidpointRounding) As Decimal': Der mit dem Argument übereinstimmende Parameter "d" wird von "String" auf "Decimal" reduziert.
'Public Shared Function Round(d As Decimal, decimals As Integer) As Decimal': Der mit dem Argument übereinstimmende Parameter "d" wird von "String" auf "Decimal" reduziert.
'Public Shared Function Round(value As Double, mode As System.MidpointRounding) As Double': Der mit dem Argument übereinstimmende Parameter "value" wird von "String" auf "Double" reduziert.
'Public Shared Function Round(value As Double, digits As Integer) As Double': Der mit dem Argument übereinstimmende Parameter "value" wird von "String" auf "Double" reduziert. C:\Users\Geldmann3\Documents\Visual Studio 2008\Projects\PowerClick\PowerClick\Form1.vb 26 33 PowerClick

Am Anfang wird "Punktzahl" als String "gedimmt". Geht das Runden mit Strings vielleicht nicht? Ich habe es aber auch schon versucht stattdessen am Anfang als "Decimal" oder "Integer" zu "dimmen". Dann zeigt Visual Basic vor dem Ausführen zwar keinen Fehler an, beim Ausführen stürzt das Programm jedoch in folgender Zeile ab.
MsgBox("Du hast " + Math.Round(Punktzahl, 0) + " Punkte erreicht, versuche es noch einmal")
Kann eine Textbox vielleicht wiederum nur Strings darstellen?
Wie muss ich das machen, dass es funktioniert?

Ich wäre für Antworten sehr dankbar:smile:

PatkIllA
2010-12-05, 19:44:03
Runden ist für Zahlen und nicht für strings. Du solltest dir mal die String-Formatierungs-Funktionen (http://msdn.microsoft.com/en-us/library/txafckwd.aspx) anschauen. Grundsätzlich gilt, dass man Zahlen nur direkt bei der Ausgabe in Strings wandeln sollte und wenn man sie wandelt sollte immer die Culture mit angegeben werden.
Was eine Funktion annimmt sagt dir doch Intellisense und ein Druck auf F1 führt einen zur MSDN.

Monger
2010-12-05, 20:20:34
Also erstens mal:

Auch wenn VB.NET das erlaubt: vermeide die implizite Typkonvertierung. Am besten schaltest du sie in den Compilereinstellungen aus. Ein Int ist eben kein Double, und auch kein String. Es schreibt sich schneller, aber ich finde, Einsteiger verwirrt es mehr als es hilft. Wenn du Typen konvertieren willst, hilft dir die "Convert" Klasse (z.B. Convert.ToInt32(Double)).

Zweitens:
Der String Operator für Konkatenationen in VB.NET heißt eigentlich "&". Also: MsgBox("Meine Zahl heißt" & zahl). Dieser Operator ruft implizit "ToString()" auf der Variable auf, deshalb sind in solchen Fällen auch andere Typen außer String erlaubt.

Drittens:
Ein String ist nur ein Text. Wenn du einen Text als Zahl interpretieren willst (merke: z.B. 4.0 ist eben keine Zahl, sondern nur die textuelle Repräsentation einer Zahl), dann musst du den String parsen. Wie z.B.
Dim meineZahl as Integer = Integer.Parse(PunktZahl)

Denk daran, dass bei textuellen Eingaben vom Benutzer du IMMER beachten musst, dass er auch was falsches eingeben kann!

Sehr verkürzt zusammengefasst müsstest du also schreiben:


MsgBox("Du hast " & Math.Round(Double.Parse(Punktzahl), 0) & " Punkte erreicht, versuche es noch einmal")


Die wahrscheinlich wichtigere Frage ist: warum hast du überhaupt Punkte mit Nachkommastellen!? ;)

Geldmann3
2010-12-06, 00:36:47
MsgBox("Du hast " + Math.Round(Double.Parse(Punktzahl), 0) + " Punkte erreicht, versuche es noch einmal")
Stürzt ebenfalls in der gegebenen Zeile ab.
Ungültige Konvertierung von der Zeichenfolge Du hast in Typ Double.

Die wahrscheinlich wichtigere Frage ist: warum hast du überhaupt Punkte mit Nachkommastellen!?
Ich dividiere und addiere nahezu zufällige Zahlen. (Abhängig von der Klickgeschwindigkeit des Spielers) Bei der Rechnung soll allerdings noch nicht gerundet werden, sondern erst danach. Man kann sogar eine negative Punktzahl erzielen:freak:

Mosher
2010-12-06, 06:59:06
Am besten du deklarierst Punktzahl als Double, rundest kurz vor der Ausgabe und castest punktzahl dann als int (damit die Zahl 1.0 als 1 erscheint). (convert, siehe oben)

Dann kanste wirklich schreiben:

MsgBox("Du hast " + punktzahl + " Punkte erreicht, versuche es noch mal!")

punktzahl ist zwar kein string, wird aber in der MsgBox vom Programm automatisch in einen "umgewandelt", wenn man so will.

Monger
2010-12-06, 08:24:14
MsgBox("Du hast " + Math.Round(Double.Parse(Punktzahl), 0) + " Punkte erreicht, versuche es noch einmal")
Stürzt ebenfalls in der gegebenen Zeile ab.
Ungültige Konvertierung von der Zeichenfolge Du hast in Typ Double.

Ich hab dir gesagt, du sollst "&" statt "+" verwenden :tongue: . Mit "+" versucht er jetzt nämlich von rechts nach links zu addieren, sieht erst ein Double, dann einen String, und sagt völlig richtig: ich kann Strings nicht mit Zahlen zusammenzählen.

Edit: habs ja selber falsch geschrieben. Sorry! Ich besser es gleich mal aus.

Kenny1702
2010-12-06, 09:32:28
Am besten du deklarierst Punktzahl als Double, rundest kurz vor der Ausgabe und castest punktzahl dann als int (damit die Zahl 1.0 als 1 erscheint). (convert, siehe oben)

Dann kanste wirklich schreiben:

MsgBox("Du hast " + punktzahl + " Punkte erreicht, versuche es noch mal!")

punktzahl ist zwar kein string, wird aber in der MsgBox vom Programm automatisch in einen "umgewandelt", wenn man so will.
Hätte ich auch vorgeschlagen, wenn CInt nicht bis einschließlich .5 abrunden würde, also CInt(1.5) => 1

PatkIllA
2010-12-06, 09:48:53
Jetzt lasst doch mal das ganze Gefummel mit runden und string zusammenbasteln
string.Format(System.Globalization.CultureInfo.CurrentCulture, "Du hast {0:0} Punkte erreicht, versuche es noch mal!", punktzahlAsDouble);

Geldmann3
2010-12-06, 10:57:59
@Monger: Stimmt, wenn ich "&" statt "+" verwende funktioniert es. Da hätte ich aufgrund deiner Hilfe auch schon selbst drauf kommen können. Um ehrlich zu sein, hatte ich mich im ersten Moment dann auch gefragt, warum du im Beispiel "+" benutzt. Ich habs aber mal einfach so übernommen:wink: Danach bin ich dann gar nicht mehr auf die Idee gekommen, dass es daran liegen könnte.

Auch das
string.Format(System.Globalization.CultureInfo.CurrentCulture, "Du hast {0:0} Punkte erreicht, versuche es noch mal!", punktzahlAsDouble);

habe ich ausprobiert, funktioniert nicht.
Der Name "punktzahlAsDouble" wurde nicht deklariert.


müsste da "punktzahlAsDouble" nicht "punktzahl As Double" heißen?

Danke, das Problem ist gelöst.

PatkIllA
2010-12-06, 11:56:55
müsste da "punktzahlAsDouble" nicht "punktzahl As Double" heißen?Ich wollte damit nur andeuten, dass die Variable ein double sein muss und nicht ein String.
Zur Ausgabe runden ist eigentlich immer schlecht, da es spätestens bei Nachkommastellen evtl. nicht geht. Außerdem kann man die Formatierungsstrings viel besser internationalisieren.

RattuS
2010-12-06, 15:11:10
Das ist mal wieder exemplarisch für VB.NET...


MsgBox(...) wird implizit zu MessageBox.Show(...), also gewöhne dir lieber gleich den objektorientierten .NET-Syntax an.
In VB.NET ist es sinnvoller mit + explizit zu konkatenieren als mit & implizite Umwandlung zu erlauben. Strikte Richtlinien werden bei Anfängern zwar oftmals als unangenehm empfunden, sind aber äußerst zweckmäßig und reduzieren mögliche Fehler erheblich.
Alle Objekte sollten bei der Ausgabe als String mit der Methode ToString() umgewandelt werden. Diese Methode steckt in allen von Object abgeleiteten Klassen, also in (fast) allen.
String.Format(...) ist eine der Methoden, die so ziemlich immer fehlerfrei jegliche Datentypen und Objekte in eine Ausgabe umwandeln (es sei mal dahingestellt, ob das auch immer sinnvoll ist). Damit liegt man im Prinzip bei der saubersten und lesbarsten Variante.


Trotzdem hier nochmal der Vollständigkeit halber eine richtige Lösung mit deiner Variablen:
MessageBox.Show("Du hast " + (Math.Round(Punktzahl)).ToString() + " Punkte erreicht, versuche es noch einmal")

PatkIllA
2010-12-06, 15:18:57
String.Format(...) ist eine der Methoden, die so ziemlich immer fehlerfrei jegliche Datentypen und Objekte in eine Ausgabe umwandeln (es mal dahingestellt, ob das auch immer sinnvoll ist). Damit liegt man im Prinzip bei der saubersten und lesbarsten Variante.wenn der Formatstring nicht zum Wert passen gibt es eine Exception. Deswegen sollte man neben dem FormatProvider auch das Format explizit angeben.
ToString ohne Parameter auf Zahlen sollte man IMO sich überhaupt nicht angewöhnen. Wir hatten schon soviele Bugs deswegen. Bei der Ausgabe auf dem Bildschirm nicht wirklich schlimm, beim Schreiben in Dateien oder sonstigen Datenaustausch absolutes Nogo.

RattuS
2010-12-06, 15:32:47
wenn der Formatstring nicht zum Wert passen gibt es eine Exception. Deswegen sollte man neben dem FormatProvider auch das Format explizit angeben.
Hatte bisher noch keine Exceptions in diesem Zusammenhang. Gib mir mal bitte ein Beispiel (ohne FormatProvider).

ToString ohne Parameter auf Zahlen sollte man IMO sich überhaupt nicht angewöhnen. Wir hatten schon soviele Bugs deswegen. Bei der Ausgabe auf dem Bildschirm nicht wirklich schlimm, beim Schreiben in Dateien oder sonstigen Datenaustausch absolutes Nogo.
Bei der Ausgabe ist das völlig egal, schließlich ist das nur GUI (wobei jeder Buchhalter natürlich wild um sich schlagen wird, wenn es keine Tausendertrennzeichen gibt ;D). Die Frage ist, warum du Zahlen als Strings austauschst (abgesehen von Schreiben in TXT/XML etc.)?

PatkIllA
2010-12-06, 15:38:12
Hatte bisher noch keine Exceptions in diesem Zusammenhang. Gib mir mal bitte ein Beispiel (ohne FormatProvider).
double doubleValue = 5.67;
string.Format("Wert {0:d}", doubleValue );Das geht zum Beispiel nicht, während es mit einem int geht.
Wenn du die DateTime Formatierungen auf andere Datentypen loslässt geht es z.B. auch nicht.
Bei der Ausgabe ist das völlig egal, schließlich ist das nur GUI (wobei jeder Buchhalter natürlich wild um sich schlagen wird, wenn es keine Tausendertrennzeichen gibt ;D). Die Frage ist, warum du Zahlen als Strings austauschst (abgesehen von Schreiben in TXT/XML etc.)?Schreiben in XML oder Textdateien ist ja schon der häufigste Anwendungsfall.

RattuS
2010-12-06, 15:51:11
double doubleValue = 5.67;
string.Format("Wert {0:d}", doubleValue );Das geht zum Beispiel nicht, während es mit einem int geht.
Wenn du die DateTime Formatierungen auf andere Datentypen loslässt geht es z.B. auch nicht.
Hm okay, aber das ist dann ja auch Unwissenheit zwecks Maskierung. Das Problem hätte man ja bei ToString(String format) auch.

Schreiben in XML oder Textdateien ist ja schon der häufigste Anwendungsfall.
Das missachten aber dann auch nur die Leute, die auch beim Auslesen direkt Konvertieren statt zu Parsen. :P

PatkIllA
2010-12-06, 15:54:44
Hm okay, ich muss dazu sagen, dass ich das so auch nicht verwenden würde. Wenn ich ein spezifisches Format brauche, formatiere ich das als Parameter. Damit ignoriere ich zwar den eigentlichen Zweck des Parsens innerhalb des Formatstrings, aber ich nutze das sowieso eher der Übersicht halber. :ulol:Das ist ja nun auch völlig sinnfrei. Es gibt so schöne, einfach zu benutzende Methoden, die genau das machen und trotzdem hacken sich soviele Leute irgendwas zurecht, was aufwändiger und fehlerbehafteter ist.
Das missachten aber dann auch nur die Leute, die beim Auslesen dieser direkt Konvertieren statt zu Parsen. ^^Was meinst du mit direkt konvertieren?
ToString und Parse geht ja zusammen, bis dann mal einer mit einer anderen Culture ankommt und alles schief geht.

RattuS
2010-12-06, 16:06:27
Das ist ja nun auch völlig sinnfrei. Es gibt so schöne, einfach zu benutzende Methoden, die genau das machen und trotzdem hacken sich soviele Leute irgendwas zurecht, was aufwändiger und fehlerbehafteter ist.
Na ja, lesbarer finde ich es nicht, wenn ich das Format direkt beim Platzhalter angebe und beim zweiten Mal draufschauen erst feststellen muss, welcher Datentyp dieser Platzhalter hat. Und fehlerbehafteter ist das auch keine Spur IMO.

Was meinst du mit direkt konvertieren?
ToString und Parse geht ja zusammen, bis dann mal einer mit einer anderen Culture ankommt und alles schief geht.
Ich beziehe mich auf Stabilität. Vorhin wurde die Convert-Klasse erwähnt, die eben kein Format nimmt, sondern direkt Exceptions schmeißt.
(Edit: Monger hat sich aber auf DecimalToInt bezogen, was in Ordnung ist.)

PatkIllA
2010-12-06, 16:19:42
Na ja, lesbarer finde ich es nicht, wenn ich das Format direkt beim Platzhalter angebe und beim zweiten Mal draufschauen erst feststellen muss, welcher Datentyp dieser Platzhalter hat. Und fehlerbehafteter ist das auch keine Spur IMO.Normalerweise hat man ja nur ein paar Parameter und auch nur einen Satz zu formatieren.
Ich beziehe mich auf Stabilität. Vorhin wurde die Convert-Klasse erwähnt, die eben kein Format nimmt, sondern direkt Exceptions schmeißt.
(Edit: Monger hat sich aber auf DecimalToInt bezogen, was in Ordnung ist.)Was meiinst du mit Stabilität? Die Convert Klasse hat die gleichen Probleme wie das Parsen und beide nehmen keinen FormatString sondern höchstens optional einen FormatProvider.
Am besten du deklarierst Punktzahl als Double, rundest kurz vor der Ausgabe und castest punktzahl dann als int (damit die Zahl 1.0 als 1 erscheint). (convert, siehe oben)
Wenn ich sowas sehe kriege ich auf jedenfalls schon mal wieder nen Hals.

Mosher
2010-12-06, 21:04:02
Vielleicht sollten wir den Threadstarter mal fragen, ob er das Programm zB für die Schule braucht, oder ob er angehender Doktor der Informatik ist.

Denn je nachdem legt der Aufgabensteller unterschiedlichen Wert auf zB Recheneffizienz des Programms, Lesbarkeit oder einfach nur den Beweis, dass der Programmierer gewissse Programmiertechniken anwenden kann.

Ich tippe eher auf letzteres, weshalb es wohl kaum sinnvol sein wird, ihm Vorshcläge á la

"Nimm doch einfach Klassenbibliothek XY, google 30 Codes, lad dir Hexconverter 298457328953467905.9 build 78888838948321575678948973425634289763428057634215792180745321894675321962580467 5321894753218946732156807321894673215948 und zum schluss: copy / paste einfach folgendes"

Glaubt ihr im ernst, wer so eine Frage wie im Eingangspost stellt, ist ernsthaft interessiert an Antworten wie:
Was meiinst du mit Stabilität? Die Convert Klasse hat die gleichen Probleme wie das Parsen und beide nehmen keinen FormatString sondern höchstens optional einen FormatProvider.

Na gut..

PatkIllA
2010-12-06, 21:20:13
Soetwas wie Formatierungen von Zahlen unter Berücksichtung von Internationalisierung ist absolute Basis. Da muss auch kein Doktor der Informatik für sein sondern selbst als nach einem Monat in der Ausbildung sollte man darauf kommen, dass Runden und dann noch in int konvertieren schlicht Pfusch ist. Zumal man mit Intellisense doch schon mal direkt die ToString Methode inkl. Überladungen angeboten bekommt und wenn man da auf Hilfe klickt wird man auch gleich auf CultureInfo und Formatierungsstrings hingewiesen.

Monger
2010-12-06, 22:06:24
In VB.NET ist es sinnvoller mit + explizit zu konkatenieren als mit & implizite Umwandlung zu erlauben.
Das Problem mit dem "+" Operator ist, dass er nunmal mehrdeutig ist. Bei Zahlen bedeutet der etwas grundlegend anderes als bei Strings.

Das Problem hat der "&" Operator nicht: der ist einzig und allein für Strings da.

Monger
2010-12-06, 22:14:10
Soetwas wie Formatierungen von Zahlen unter Berücksichtung von Internationalisierung ist absolute Basis.

Also ganz ehrlich: die ganze Globalisierungsthematik ist jetzt nun wirklich kein Einsteigerthema. Welche Klassen jetzt IFormatProvider implementieren, und wann String Vergleiche mit neutraler Culture und wann mit lokaler Culture gemacht werden sollten, und warum ToUpper besser als ToLower ist, und wie man String.Format geschickterweise zur Lokalisierung von Fehlermeldungen mit Resourcen nutzt...

In das Thema kann man sich beliebig tief reinknien. Ich kann es nachvollziehen, warum ein Anfänger lieber selber Strings konkateniert als String Formatierer zu nutzen.


Dass die eigentliche Ursache des Problems hier darin liegt, dass überhaupt mit Gleitkommawerten gerechnet wird - das sehe ich genauso wie du.

Geldmann3
2010-12-06, 22:15:56
Das Programm ist übrigens nur für private Übungszwecke. Auch wenn ich noch nicht alles was hier geschrieben wird gleich begreife, finde ich es doch interessant.
Wenn ich in einiger Zeit auf diesen Thread zurückkomme könnten die Vorschläge hilfreich sein. Sicher auch, für Leute die ähnliche Probleme haben.;)

PatkIllA
2010-12-06, 22:44:51
Für den Hausgebrauch reicht ja normalerweise zwischen CultureInfo.InvariantCulture und CultureInfo.CurrentCulture zu unterscheiden und bei den Formatstrings zu unterscheiden wie viele Nachkommastellen ausgegeben zu werden.
Ich finde es halt ziemlich wichtig, dass man die Umgebung kennt und dann nicht irgendwelche komischen Konstrukte verbaut, um es irgendwie hinzubekommen. Da fällt am Ende meistens ein unwartbarer Haufen Code raus, der wie ein Mikadospiel bei der kleinsten Änderung in sich zusammenfällt. Da wird dann an viel zu vielen Stellen mit Strings gearbeitet, statt die eigentlichen Objekte zu nehmen. Ellenlange Arrays durchgereicht, XML mit Stringoperationen geparsed und so ein Krams. Allgemein sollte man halt merken, dass die meisten Probleme, die man hat schon von jemanden anders gelöst wurden und moderne Frameworks einem da schon ziemlich unter die Arme greifen. Sowas wie Textausgabe oder XML lesen sind da ganz gute Beispiele.

RattuS
2010-12-06, 23:06:34
Ich muss dir hier einfach in einigen Punkten widersprechen.

Für den Hausgebrauch reicht ja normalerweise zwischen CultureInfo.InvariantCulture und CultureInfo.CurrentCulture zu unterscheiden und bei den Formatstrings zu unterscheiden wie viele Nachkommastellen ausgegeben zu werden.
Natürlich dürfte jedem Programmierer, unabhängig von seiner Erfahrung, sofort klar sein, wie wichtig die Bedeutung der kulturellen Umgebung ist. Aber praktisch sieht es so aus, dass die meisten Anwendungen sowieso Fachsoftware sind, meist noch auf den Kunden zugeschnitten, und im Regelfall auf ein und der selben Sprachumgebung laufen. Natürlich hast du trotzdem recht, was CultureInfo angeht.

Ich finde es halt ziemlich wichtig, dass man die Umgebung kennt und dann nicht irgendwelche komischen Konstrukte verbaut, um es irgendwie hinzubekommen. Da fällt am Ende meistens ein unwartbarer Haufen Code raus, der wie ein Mikadospiel bei der kleinsten Änderung in sich zusammenfällt. Da wird dann an viel zu vielen Stellen mit Strings gearbeitet, statt die eigentlichen Objekte zu nehmen. Ellenlange Arrays durchgereicht, XML mit Stringoperationen geparsed und so ein Krams. Allgemein sollte man halt merken, dass die meisten Probleme, die man hat schon von jemanden anders gelöst wurden und moderne Frameworks einem da schon ziemlich unter die Arme greifen. Sowas wie Textausgabe oder XML lesen sind da ganz gute Beispiele.
Meiner Meinung nach genau das falsche Konzept, um Beginnern das Programmieren beizubringen. Programmieren heißt nicht zu wissen, was das Framework kann, sondern zu wissen, wie man Sachverhalte analysiert und Problemlösungen schafft. Lieber lasse ich mir von einem Azubi eine völlig neue Datumsklasse schreiben, als ihn mit dem GregorianCalendar (Java) herumfuchteln zu sehen, dessen Snippet er von irgendeiner Website kopiert hat. Natürlich, man muss das Rad nicht immer neu erfinden, aber manchmal ist Unabhängigkeit wertvoller als Komfort. :)

Übrigens finde ich es viel schlimmer, wenn, gerade Umsteiger von VB-Classic, sich schlichtweg stur stellen, wenn es um Objektorientierung geht. Ich kenne viele junge Programmierer, die immer noch auf flache Arrays und statische Methoden setzen und sich gegenüber den flexiblen Möglichkeiten des .NET nicht öffnen. Aber die Diskussion gerät hier, zumindest meinerseits, etwas off-topic.

PatkIllA
2010-12-06, 23:18:30
Und zum Problemlösungen schaffen heißt es auch die Umgebung zu kennen. Und gerade bei Datumsberechnungen würde ich sowas von stark davon abzuraten irgendwas selbst zu schreiben, da man da auf jeden Fall, irgendwas nicht beachtet.
Zum allgemeinen Programmieren lernen kann man sicher mal was nachprogrammieren, um das Verständnis zu schaffen, aber wenn man das in der Praxis einsetzt sollte man IMO eher die Finger davon zu lassen. Insbesondere wenn sie zum Framework von Java oder .NET gehören.

Die Unart alles mögliche in Arrays zu packen läuft mir auch immer mal wieder über den Weg.