C# Richtige Zuweisung von enums via out oder ref

Wutknubbel

Cadet 3rd Year
Registriert
Dez. 2014
Beiträge
56
Hallo Zusammen.

Ich steht gerade etwas auf den Schlauch und verstehe da eine Eigenart von C# nicht so recht.

Ich habe 2 Enums von Typ uint und möchte sie in einer Funktion zuweisen, optimalerweise wollte ich mit out oder ref
arbeiten, aber irgendwie kommt nicht das Gewünschte Ergebnis raus

C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Project1
{
    internal class Program
    {
        [Flags()]
        public enum TestA : uint
        {
            TestA_1 = 0x00000000,
            TestA_2 = 0x00000001,
            TestA_3 = 0x08001000,
            TestA_4 = 0x08001100
        }

        [Flags()]
        public enum TestB : uint
        {
            TestB_1 = 0x00000000,
            TestB_2 = 0x00000001,
            TestB_3 = 0x08001000,
            TestB_4 = 0x08001100
        }

        public static void Main(String[] args)
        {
            TestA ausgabe_a=0;
            TestB ausgabe_b=0;

            GetFlags(ref ausgabe_a, ref ausgabe_b);

            Console.WriteLine("Bestanden!");
        }

        private static void GetFlags(ref TestA werta, ref TestB wertb)
        {
            werta = TestA.TestA_2 | TestA.TestA_3 | TestA.TestA_4;
            wertb = TestB.TestB_3 | TestB.TestB_4;
        }
    }
}

Wenn ich das Programm Debugge kommt bei ausgabe_a = TestA.TestA2 | TestA.TestA4
und bei ausgabe_b = TestB.TestB_4 raus

Richtig sollte es sein ausgabe_a = TestA.TestA_2 | TestA.TestA_3 | TestA.TestA_4;
und bei ausgabe_b = TestB.TestB_3 | TestB.TestB_4;

Vielleicht kann mich ja jemand mal auf den richtigen weg schubsen :)

Ps. mit out geht es auch nicht soviel hab ich schonmal herausgefunden

Vielen dank schonmal für die Hilfe.
 
Ob ref oder out ist in deinem Fall nicht das Problem.

Die bits in TestA.TestA_3 sind eine Teilmenge der bits in TestA.TestA_4, also kann man nicht unterscheiden ob TestA_3 und TestA_4 oder nur TestA_4 gesetzt sind..

TestA.TestA_3 | TestA.TestA_4 ist das gleiche wie TestA.TestA_4, da TestA_3 keine zusätzlichen bits setzt.

Gleiches Problem mit TestB_3/4.
 
  • Gefällt mir
Reaktionen: Wutknubbel und Tornhoof
schalli110 hat es ja schon beschrieben, nochmal mit Werten um die Mathematik deutlicher werden zu lassen, (Das |-Zeichen ist ein logisches oder, also es werden jeweils die Bits verglichen und wenn eines 1 ist, ist das Ergebnis-Bit an der Stelle auch 1, ansonsten 0):
0x08001000 | 0x08001100 = 0x08001100
und damit
TestB_3 | TestB_4 = TestB_4

Für Flags sind deswegen normalerweise alle Werte nur ein einzelnes gesetztes Bit an einer bestimmten Position.
(siehe z.B. https://riptutorial.com/csharp/example/6327/binary-literals)
 
  • Gefällt mir
Reaktionen: Wutknubbel
Danke nochmals, ich werde mich da mal ein bisschen durcharbeiten.
 
Und nicht zu viel von dem Flags-Attribut erhoffen, das sorgt eigentlich nur für ein hübscheres ToString().

Aber der Kern ist schon gesagt: Die Summe der |-Operationen lässt sich nicht mehr eindeutig auf die Bestandteile zurückführen. Du könntest allerdings auch einfach alle Enumerationswerte durchprobieren mit HasFlag() und kannst so ermitteln, welche davon gesetzt sind.
 
  • Gefällt mir
Reaktionen: mibbio
Bei Flags nutzt man nur die 2er-Potenzen (2, 4, 8, 16, ...) und keine Zwischenwerte, damit die einzelnen Flags eindeutig sind. Also pro Flag nur 1 Bit gesetzt, die Zwischenwerte entstehen nur durch Kombinationen von Flags und sind durch die Verwendung der 2er-Potenzen dann auch eindeutig.

Dann wäre beispielsweise 0010 & 0100 (dezimal: 4 + 8) 0110 (12) und da es kein Flag gibt, das alleine 12 wäre, ist damit garantiert, dass nur Flag 1 und Flag 2 gemeint sein kann. Würde man jetzt aber für ein Flag 3 direkt 0110 setzen, kann man hinterher beim Auswerten nicht mehr differenzieren, ob das die Kombination aus Flag 1 & 2 darstellen soll oder Flag 3.
 
  • Gefällt mir
Reaktionen: Renegade334
DaZpoon schrieb:
Und nicht zu viel von dem Flags-Attribut erhoffen, das sorgt eigentlich nur für ein hübscheres ToString().
Das ist natürlich Käse, man kann sicherlich Flags mißbrauchen, aber überall dort, wo mehrdimensionale Zustände angenommen werden können, da machen Flags Sinn.

Ansonsten wurde prinzipiell alles gesagt … nur noch soviel, manchmal hat man eine Situation mit Abstufungen oder Korrelationen zwischen Elementen im Enum.
Dann kann ich, wenn ich will und wenn es öfter benötigt wird, natürlich „Sammeleinträge“ bauen, um den folgenden Aufwand zu reduzieren oder seinen Code sprechender zu halten.

Beispiel, ich hab Enum ConnectionStyle und habe Secured, Unsecured und zusätzlich noch Ssl3, Tls10 usw.
Dann kann mein ConnectionStyle.Secured durchaus ein bitweises Oder aus den einzelnen Tls, SSL etc Einträgen sein (oder Teilen davon).
 
@RalphS : Genau lesen , ich meinte das Attribut [Flags]. Dessen Verwendung verändert effektiv nur das ToString(), funktional alles wie wenn man das Attribut nicht verwendet. Man muss sich also z.B. auch selbst um die korrekten Enumerationswerte kümmern. Beispiel:

C#:
[Flags]
enum TestEnum
{
    ValueA,
    ValueB
}

...

    TestEnum flags = TestEnum.ValueB;

    flags.HasFlag(TestEnum.ValueB); // true
    flags.HasFlag(TestEnum.ValueA); // true <-- Unerwartet
 
  • Gefällt mir
Reaktionen: Wutknubbel und mibbio
Hallo Zusammen

Danke nochmals für die Infos, ich denke mal ich hab es in Ansatz verstanden, und werde mich in den
Thema nochmal vertiefen.

Das Beispiel oben, wahr nur fix geschrieben, zeigte mir aber das selbe Problem wie ich es
im original Projekt auch habe, deswegen habe ich es auch heruntergekürzt.
 
DaZpoon schrieb:
Genau lesen , ich meinte das Attribut [Flags]. Dessen Verwendung verändert effektiv nur das ToString()

Ah, ich sehe wo das Problem ist.
Das FLAGS Attribut ist eine AUSZEICHNUNG. Ein Attribut eben. Man setzt es um zu SEHEN daß es da um Flags geht, daß also der Enum ein Bitfeld sein soll.

Normale Enums sind einfach Auflistungen von den Schlüsselnamen. Der Rest passiert automatisch im Hintergrund.
Es sind insbesondere KEINE Bitfelder. Wurde ja auch schon eingehend diskutiert.

Was für ein ToString() implementiert ist, ist außer für Powershell piepegal. Notfalls selber überschreiben. Wenn DAS der Auftrag des FLAGS Attributs gewesen wäre, dann hätte man es aus der Sprachspezifikation weglassen können.
 
Zurück
Oben