| 

.NET C# Java Javascript Exception

7
Angenommen, ich benötige eine Variable, die Werte von 0 - 511 speichern kann. Entspricht es dann gutem Design, einen Datentypen zu wählen, der dafür angemessen ist, oder würdet ihr einfach int nehmen?

Falls nicht:
Da die Zahlen ausschließlich positiv sein werden, tendiere ich zu ushort. Aber ich sag mal ganz ehrlich: Solchen Deklarationen begegne ich verblüffend selten... es ist, als würde es keinen mehr interessieren. :)

Wie steht ihr dazu?

Update:
Konkret geht es um die Umsetzung eines Spielalgorithmus zu TicTacToe. Für das einfache Speichern der neun Felder wird eine Zahl verwendet, in der die Position der angekreuzten Felder als Bits (1) markiert sind. Entsprechend gibt es für jeden Spieler eine Zahl, die das Spielfeld im Speicher repräsentiert.

9 Felder = 9 Bits = max. 511 (111 111 111).

Es gibt tatsächlich eine List<int>. Und zwar mit allen möglichen Gewinnerkombinationen. Z.B. 111 000 000 für die oberste Reihe. Dezimal = 448, etc.

Sprich:
private List<int> winList = new List<int> { 448, 56, 7, 292, 146, 73, 27, 84 };


Für das Positionieren eines angekreuzten Feldes reicht eine einfache binäre Oder-Verknüpfung:
private int area = 0
...
area |= 1 << position // Position : 0..8


Und für das Ermitteln, ob der Spieler gewonnen hat eine einfache binäre Und-Verknüpfung:

...
return winList.Exists(x => ((area & x) == x));


Natürlich erreiche ich hier keine Datenmengen, die bei Nutzung des Typs Int32 für Probleme sorgen würden. :) Die Masterfrage ist nur: Wenn es zu einem guten Design gehört, passende Datentypen zu nutzen und dabei nicht gleich mit Kanonen auf Spatzen zu schießen, dann sollte das zu den "Programmierreflexen" gehören... ansonsten denkt man in wichtigen Zusammenhängen auch nicht daran, oder?
14.11.2012
Mephisztoe 111 4
5 Antworten
4
Allgemein

Das .Net-Framework ist in diesem Punkt sehr Inkonsequent. Es gibt Funktionen die Byte und Int16 verwenden, z.B. Guid Constructor (Int32, Int16, Int16, Byte, Byte, Byte, Byte, Byte, Byte, Byte, Byte), und welche die es nicht tun Color.FromArgb Method (Int32, Int32, Int32, Int32). Die Empfehlungen ob man Alias-Typen oder Laufzeittypen verwenden soll gehen weit auseinander. Im allgemeinen kann man jedoch sagen das die Verwendung von Laufzeittypen mehr Informationen enthalten als ihre Alias-Typen. Ein Beispiel: long = Int64, short = Int16.

Performance und Speicherverbrauch

Grundsätzlich ist Int32 immer dann zu bevorzugen wenn des um Performance geht. Die CLR ist daraufhin optimiert mit 32-Bit Integer-Datentypen zu arbeiten. Am Beispiel von Short lässt sich das gut erklären. Short (bzw. Int16) sollte nur 2 Byte benutzen, wird aber von der CLR im Evaluation Stack als Int32-Wert gespeichert was also 4 Bytes Speicherplatz bedeutet. (Siehe dazu: Expert .NET 2.0 IL Assembler).

Zum Speicherverbrauch. Ungeachtet der soeben erwähnten Tatsache sind kleiner Datentypen wie Int16 trotzdem sinnvoll. Sobald man mit Array's oder Listen arbeitet verhällt sich die CLR wieder anders und speichert Beispielsweise ein short wirklich nur in 2 Bytes.

Aus dieser Sicht folgenden Zusammenfassung:

Perfomace: Int32
Arrays: Int16, Byte, etc.

Soviel zur Theorie.

Meine Meinung

Getreu dem Motto "premature optimization is the root of all evil" (Führzeitige Optimierungen sind die Wurzel allen Übels) gehe ich persönlich lieber auf die spezifischen Datentypen mit den passenderen Wertebereichen da diese den Code besser lesbar machen. Beim Vergleich des GUID-Constructors mit der Color.FromArgb-Methde sieht man das sehr schön.
Performance-Bremsen und Speicherprobleme sollte man lieber mit einem Profiler lösen statt sich von Anfang an auf obscure Implementierungen in der CLR zu beziehen. In der Regel merkt man den Unterschied an diesem Detail eh nur bei sehr großen Arrays.

Alternativ und in vielen Fällen sogar die bessere Lösungen wäre es, eigene Datentypen zu Implementieren die den korrekten Wertebereich darstellen. Intern würde ich aber auch hier erstmal auf die spezifischen Datentypen setzten. Im Notfall (wenn ein Profiler an dieser Stelle wirklich mal ein Problem finden sollte, kann ich dies weitesgehend transparent für alle Verwendungsstellen ändern).
14.11.2012
Floyd 14,6k 3 9
Floyd 14,6k 3 9
"wird es im allgemeinen empohlen Alias-Namen für Typen nicht zu verwenden" - wer empfiehlt das? Ich meine das Gegenteil gelesen zu haben und habe auf die Schnelle diese Referenz gefunden:

"ECMA-334 C# lang spec (page 18):

Each of the predefined types is shorthand for a system-provided type. For example, the keyword int refers to the struct System.Int32. As a matter of style, use of the keyword is favored over use of the complete system type name."

(zitiert nach:
http://stackoverflow.com/questions/62503/c-int-or-int32-should-i-care
)
Matthias Hlawatsch 14.11.2012
Der Verweis auf den Guid-Konstruktor ist schön - allerdings ist das ein Fall, wo einer der eingebauten Datentypen genau den passenden Wertebereich hat, was in der Frage ja gerade nicht der Fall ist.

Einen eigenen Datentypen im Nachhinein einzubauen halte ich für relativ aufwendig und riskant. Diese Entscheidung würde ich eher früh zu treffen versuchen.
Matthias Hlawatsch 14.11.2012
"The two are indeed synonymous; int will be a little more familiar looking, Int32 makes the 32-bitness more explicit to those reading your code."
http://stackoverflow.com/questions/62503/c-int-or-int32-should-i-care

"Using the aliased type names sbyte, byte, short, ushort, int, uint, long, ulong, float, and double instead of SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, and Double respectively primarily improves code readability."
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/014f9924-fc25-4f48-a9c5-b3e86566a24a
Floyd 14.11.2012
Aber ich werd den Satz kurz umschreiben :)
Floyd 14.11.2012
"Einen eigenen Datentypen im Nachhinein einzubauen halte ich für relativ aufwendig und riskant." Richtig, wenn du das so verstanden hast, dann ist das falsch rüber gekommen.

Was ich meinte:

1. Ich würde eigene Datentypen verwenden (z.B. "Gewinnerkombinationen").
2. Intern (im eigenen Datentyp) wurd ich die passenden Datentypen verwenden. (Int16)
3. Die internen Datentypen (innerhalb meines Datentyps) kann ich notfalls immernoch auf einen anderen Umstellen wenn dies notwendig ist.
Floyd 14.11.2012
1
Nach dem Update Deiner Frage: der passende Datentyp wäre dann aus meiner Sicht ein BitArray der Länge 9. Denn Du willst ja gar nicht wirklich mit Zahlen zwischen 0 und 511 hantieren, sondern mit einer Folge von 9 Bits. Und

area.Set(position, true);

finde ich auch besser lesbar als

area |= 1 << position;


Update:
Die Liste mußt Du nur einmal initialisieren, und ich finde, hier wäre etwas zusätzliche Schreibarbeit die deutlich höhere Verständlichkeit wert:

private const bool x = true;
private const bool o = false;

private readonly List<BitArray> winList = new List<BitArray>()
{
new BitArray(new bool[]{
x,o,o,
o,x,o,
o,o,x }),
new BitArray(new bool[]{
x,x,x,
o,o,o,
o,o,o }) //, ...
};


Das Suchen ist mit einer kleinen Helfer-Methode ganz einfach:

private static bool Matches(BitArray pattern, BitArray arrayToTest)
{
for (int i = 0; i < pattern.Length; i++)
{
if (!(pattern[i] && arrayToTest[i] == arrayToTest[i]))
return false;
}

return true;
}

...
bool isWinner = winList.Exists(p => Matches(p, area));


Zugegeben, das sind ein paar Zeilen Code mehr als Deine Lösung. Aber mir wäre die Lesbarkeit wichtiger. Ich glaube, die meisten Anwendungsentwickler haben nicht den nötigen "Matrix-Blick", um bitweise boolsche Verknüpfungen und Shift-Operationen flüssig in die dahinterstehende Spielfeld-Semantik übersetzen zu können oder die Zahl 292 in ein Muster aus angekreuzten Spielfeldern.
14.11.2012
Matthias Hlawatsch 13,2k 4 9
Einverstanden. Wie schaffe ich es aber mit einem BitArray, auf einfache Weise eine Liste von Mustern auf Vorkommen zu überprüfen?

Wenn ich dazu nun eine List<BitArray> definieren müsste mit 8x ...new BitArray(true, true, true, false, false,....) etc. wäre das nicht unbedingt, was ich erreichen wollte. :) Da finde ich meine Lösung eleganter.
Mephisztoe 14.11.2012
Siehe mein Update.
Matthias Hlawatsch 14.11.2012
@Matthias, die Idee mit den Konstanten gefällt mir. So wird es wirklich leserlich. Auch der Grundgedanke hinsichtlich Anforderung <-> Datentyp ist nachvollziehbar. Schöne Lösung!
Mephisztoe 14.11.2012
@Matthias: Kleine Korrektur an Deinem Code-Sample:
if (!((pattern[i] & arrayToTest[i]) == arrayToTest[i])) return false;

So klappts.
Mephisztoe 16.11.2012
0
Ich würde wohl auch eher zur Verwendung eines Int32 (int) tendieren, als mich auf die wohl wirklich sehr selten verwendeten Alternativen wie UInt16 (ushort) zu stürzen.

Teilweise mag das aber auch möglicherweise daran liegen, dass Microsoft es uns genau so vormacht (Siehe MSDN: Color.FromArgb-Methode)

Andererseits bin ich schon vielen Hobby-Entwicklern begegnet, die diese ganzen selten genutzten Datentypen gar nicht kannten. Vielen ist "int" der einzig bekannte Datentyp für Ganzzahlen, über den Speicherplatz wird seltenst nachgedacht (was bei heutigen Maschinen und diesen "niederen" Datentypen eh keinen Unterschied macht).
Interessant wird das ganze bei der Anlage von Listen (z.B. List<int>) und einer entsprechend großen Datenmenge... da würde ich dann anfangen, mir Gedanken zu machen, ob ich wirklich ein int brauche, oder ob ein Short vllt. schon reicht.

Um auf die Ausgangsfrage zurückzukommen:
Ich würde zu Int32 tendieren und die den Wert verarbeitenden Methoden mit entsprechender Ausnahmebehandlung versehen. Auf Anhieb fällt mir jetzt eh kein Datentyp ein, der sich wirklich für diesen Wertebereich eignet... abgesehen davon du würdest einen eigenen Datentyp implementieren ;)

so far
Karill Endusa
14.11.2012
Karill Endusa 1,5k 1 9
0
Wenn der Datentyp fachlich begründet ist, dann bin ich für explizite Typisierung. Einen Integer zu verwenden, und überall Validierung einzufügen, ist IMHO keine gute Idee. Ist auch nicht robust gegenüber Veränderung.

Eine struct zu definieren und ein paar Operatoren zu überladen, um Arithemtik mit "int" zu ermöglichen, impliziter Cast-Operator, um den Datentyp castfrei einem int zuzuweisen,... ist nicht viel Arbeit und hält den Zeichen der Zeit besser Stand.

UPDATE :
Im Hinblick auf Dein Update sehe ich das Problem gar nicht gegeben. Deine Wahl des auf 512 Werte eingeschränkten Ganzzahltyps ist ein Implementierungsdetail Deiner TicTacToe Implementierung.
Der Beispiel-Code (Bits schubsen) ist (so verstehe ich das hoffentlich richtig), die Implementierung des Spiels. Das Spiel selbst ist dann über eine Abstraktion verwendbar, an deren Schnittstelle dieser Datentyp nicht mehr auftaucht. Damit sehe ich auch keine Notwendigkeit expliziter Typisierung. Ob dann Int16, Int32, UInt16, ... wäre mir persönlich egal und ich würde tendenziell zum "int" greifen.
14.11.2012
ffordermaier 8,4k 3 9
"nicht viel Arbeit" - dem stimme ich nicht zu. Es gibt eine Menge zu tippen und zu überlegen. Sehr lehrreich finde ich:

http://blogs.msdn.com/b/lucabol/archive/2007/12/24/creating-an-immutable-value-object-in-c-part-iii-using-a-struct.aspx
Matthias Hlawatsch 14.11.2012
"Nicht viel Arbeit" ist nicht auf die Erstellung des Typs alleine bezogen, sondern auch auf die Wartbarkeit und Evolvierbarkeit im Produktlebenszyklus. Ein (U)Int(16, 32, egal) mit eigener Semantik (auch wenn es anfangs NUR der Wertebereich ist) wird von mir explizit typisiert.
Dass ich dabei nachdenken und tippen muss - das ist nun mal unser Business.
ffordermaier 14.11.2012
Ich habe Dich aber offensichtlich mit der Bezeichnung "struct" verwirrt, die ich hier salopp als Synonym für Werttyp verwendet habe. Besser wäre wohl ValueObject o.Ä. gewesen.

Der Hinweis, dass oberflächlich simpel anmutende Dinge im Detail oft sehr diffizil sind, ist aber sicher richtig. Das ist ein Thema für ein Team-Dojo...
ffordermaier 14.11.2012
0
Die Antwort ist natürlich ein klares "kommt drauf an". Auf die Anforderungen natürlich ;-) Wenn Du mit dem Speicher knausern mußt oder es Dir wichtig ist, dass der Compiler die Zuweisung eines viel zu großen oder negativen Wertes anmeckert, könnte ushort hier die richtige Wahl sein. Wobei der zweite Punkt recht schwach ist, denn Werte von 512 bis 65535 würden akzeptiert werden, obwohl sie in Deinem Kontext schon ungültig sind. Wenn der Typ aber in einer öffentlichen API auftaucht (ushort bzw. UInt16 ist nicht CLS-kompatibel!) oder Du viel damit rechnen mußt (die Summe zweier ushorts ist ein int, der explizit gecastet werden muß), würde ich doch eher int nehmen. Ob es für Dich eher positiv oder negativ ist, wenn die meisten Leser Deines Codes stutzen und sich fragen: wieso denn ushort?, mußt Du entscheiden.

Oder generell: eigene Datentypen sind oft sehr nützlich, wenn sie gut implementiert sind. Und das ist meist schwieriger, als man auf den ersten Blick glaubt. Es lohnt sich vor allem für fachlich motivierte Datentypen (ISBN, KFZ-Kennzeichen, Vektor, Matrix...), die im Programm häufig verwendet werden. Wenn ich keinen eigenen Datentyp bauen will, nehme ich von den vorhandenen den, der "am besten" paßt, und dass dieses "am besten" eine Abwägung ist, habe ich eingangs versucht zu skizzieren.
14.11.2012
Matthias Hlawatsch 13,2k 4 9

Stelle deine .net-Frage jetzt!
TOP TECHNOLOGIES CONSULTING GmbH