C# Variable darf nur bestimmte vordefinierte Werte annehmen

Krik

Fleet Admiral Pro
Registriert
Juni 2005
Beiträge
17.015
Moin,

gibt es in C# ein einfaches Konstrukt, mit dem ich das realisieren kann?

Es gibt eine Variable CellType, die die Werte underWater, land, shore, mountain annehmen kann. Dabei dürfen auch mehrere Werte gleichzeitig gesetzt werden (eben wenn mehreres zutrifft):
underwater schließt land und shore aus.
land schließt underWater aus.
shore schließt underWater aus.

So etwas wie underWater + mountain ist erlaubt.


Ich hab es erst mal über die alte Bitmuster-Variante gelöst:
Code:
public static ulong UNDERWATER = 0;
public static ulong LAND = 1;
public static ulong SHORE = 2 + 1; // Küste = Land
public static ulong MOUNTAIN = 4;

private ulong CellType;
Die Werte werden einfach ver-ODER-t.
Ignoriert das ulong, das steht erst mal nur pauschal hier. Ich weiß, dass es oversized ist.


Bei dieser Variante kann ich aber Dinge wie underWater + land nur über die Setter-Methode mit zig Ifs ausschließen. Daher bin ich auf der Suche nach einem Konstrukt, bei dem ich auf einfache Art explizit die erlaubten Kombinationen angeben und die unerlaubten Kombinationen ausschließen kann.


Gruß, Laurin
 
Zuletzt bearbeitet: (UND und ODER vertauscht)
Das ist eine andere Variante meines Bitmusters, aber es schließt gewisse Kombinationen nicht aus.
 
Hi e-Laurin,
ich hab da mal was zusammengebastelt. Vielleicht hilft dir das ja.

Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CellAnwendung
{
    [Flags]
    public enum CellTypeEnum
    {
        Underwater = 0x01,     // 0000 0 0 0 1
        Land = 0x02,            // 0000 0 0 1 0
        Shore = 0x04,           // 0000 0 1 0 0
        Mountain = 0x08         // 0000 1 0 0 0
    }

    public class CellTypeNotAllowedException : ApplicationException
    {
        public CellTypeNotAllowedException() { } 
        public CellTypeNotAllowedException(string message):base(message) { }
        public CellTypeNotAllowedException(string message, Exception inner):base(message, inner) { } 
    }

    public class CellType
    {
        int _totalCellType = 0;
        List<int> _restricted;
        List<CellTypeEnum> _cellTypeList;

        public CellType(CellTypeEnum firstCellType)
        {
            _cellTypeList = new List<CellTypeEnum>();
            _restricted = new List<int>();

            SetRestricedTypes();

            _cellTypeList.Add(firstCellType);
            _totalCellType = _totalCellType | (int)firstCellType;
        }

        private void SetRestricedTypes()
        {
            AddRestricedType(CellTypeEnum.Underwater, CellTypeEnum.Land);
            AddRestricedType(CellTypeEnum.Underwater, CellTypeEnum.Shore);
        }

        public void AddRestricedType(params CellTypeEnum[] typen)
        {
            int restricted = 0;

            for (int i = 0; i < typen.Length; i++)
            {
                restricted = restricted | (int)typen[i];
            }

            _restricted.Add(restricted);
        }

        private bool IsRestriced(CellTypeEnum cellType)
        {
            foreach (int restricedType in _restricted)
            {
                if (((_totalCellType | (int)cellType) & (int)restricedType) == (int)restricedType)
                {
                    return true;
                }
            }

            return false;
        }

        public void AddCellType(CellTypeEnum cellType)
        {
            if (!IsRestriced(cellType))
            {
                _cellTypeList.Add(cellType);
                _totalCellType = _totalCellType | (int)cellType;
            }
            else
            {
                throw new CellTypeNotAllowedException();
            }
        }

        public void RemoveCellType(CellTypeEnum cellType)
        {
            if (_cellTypeList.Contains(cellType))
            {
                _cellTypeList.Remove(cellType);
                _totalCellType = _totalCellType ^ (int)cellType;
            }
        }

        public string GetCellTypeString()
        {
            return Convert.ToString(_totalCellType, 2).PadLeft(4,'0');
        }

        public int GetCellTypeInt()
        {
            return _totalCellType;
        }

        public List<CellTypeEnum> GetCellTypeList()
        {
            List<CellTypeEnum> copyList = new List<CellTypeEnum>();
            foreach (CellTypeEnum cellType in _cellTypeList)
            {
                copyList.Add(cellType);
            }

            return copyList;
        }
    }
}

Wenn du was nicht verstehst kannst du ja fragen.

Gruß, Kami
 
Ehrlich, es ist sehr nett, dass du mir gleich eine ganze Klasse bastelst. Allerdings ist das viel zu komplex.


Ich habe auf so etwas einfaches wie das gehofft, aber das scheint es nicht zu geben:
(Pseudo-Code)
Code:
public CellType
{
    underWater = 0;
    land = 1;
    shore = 2 + 1;
    mountain = 4;

    restricted
    {
        water, land;
    }
}
 
Vielleicht überleg ich mal noch wie es einfacher klappen könnte.
Du kannst ja auch einfach die wichtigsten Sachen von mir übernehmen.
Wichtig ist ja eigentlich nur die Liste der nicht erlaubten Typen und das überprüfen.
Wenn dir das reicht übernimm einfach nur die beiden Methoden.

Noch eine Frage.
Du hast ja gesagt du willst eine Bitmaske benutzen, aber irgendwie passen deine werte nicht zu einer Bitmaske, oder ich verstehs gerade nur einfach nicht :D
 
Zuletzt bearbeitet:
Ah stimmt. Wasser darf nicht auf 0 stehen, da ja sonst kein Bit gesetzt ist.
 
Ich raffe nich was du es dir so schwer machst.
Mach dir doch ne Klasse wo du nur die erlaubten abbildest und alles andere Ignorierst du eiskalt.
 
Ich bins noch mal, hab noch etwas drüber nachgedacht wie man das verkleiner kann und vielleicht ist das ja kurz genug.
Mich würde aber auch interessieren wie man das ganz anders lösen kann. Gerade das mit dem Property interessiert mich sehr, weil Property kenn ich jetzt nur als get/set, oder meinst du was anderes?

Code:
class CellTypeSimple
    {                                       //                  M S L U
        public static int UNDERWATER = 1;   //                  0 0 0 1
        public static int LAND = 2;         //                  0 0 1 0
        public static int SHORE = 6;        // Küste = Land     0 1 1 0
        public static int MOUNTAIN = 8;     //                  1 0 0 0

        static int[] restricted = new int[] { (UNDERWATER | LAND), (UNDERWATER | SHORE) };

        public static int AddType(int total, int cellType)
        {
            foreach (int restricedType in restricted)
            {
                if (((total | cellType) & restricedType) == restricedType)
                {
                    return total;
                }
            }

            return (total | cellType);
        }
    }

//Beispielanwendung
main()
        {
            int cell = CellTypeSimple.LAND;
            cell = CellTypeSimple.AddType(cell, CellTypeSimple.MOUNTAIN);
            cell = CellTypeSimple.AddType(cell, CellTypeSimple.UNDERWATER);

            cell = CellTypeSimple.MOUNTAIN;
            cell = CellTypeSimple.AddType(cell, CellTypeSimple.UNDERWATER);

            cell = CellTypeSimple.SHORE;
            cell = CellTypeSimple.AddType(cell, CellTypeSimple.UNDERWATER);
        }

Gruß, Kami
 
Zuletzt bearbeitet:
@e-Laurin

So wie du dir das vorstellst klappts (afaik) nicht.
Ne einfache und imo auch recht schöne Lösung wäre ne Extension Method für CellType zu schreiben:
PHP:
[Flags]
public enum CellType
{
    UnderWater = 0x1,
    Land = 0x2,
    Shore = 0x6, // Shore = Land
    Mountain = 0x8
}

public static class CellTypeExtensions
{
    public static CellType AddType(this CellType ct, CellType type)
    {
        if (ct.HasFlag(CellType.UnderWater))
        {
            type &= ~CellType.Shore;
            type &= ~CellType.Land;
        }

        if (ct.HasFlag(CellType.Land) || ct.HasFlag(CellType.Shore))
        {
            type &= ~CellType.UnderWater;
        }

        return ct | type;
    }
 }
 
Zuletzt bearbeitet: (Fehler ausgebessert)
@Kamikaze84 & Grantig
Auch hier wieder vielen Dank, dass ihr euch so viel Mühe gemacht habt.

Ich hadere im Moment, ob ich nicht doch einfach eine Prüfung im Setter einprogrammiere oder die Lösung von Grantig nehme. Oder ich verhindere es schon im Vorfeld, sodass gar keine Prüfung mehr notwendig ist.
Ich lass die Sache am besten erst mal aus, bis ich mehr vom Rest des Programms umgesetzt habe. Dann wird mir vermutlich klar, was hier in meinem Fall die beste Idee ist.

Ich bedanke mich bei allen, die zu diesem Thema beigetragen haben. :)


Es wäre aber schon genial, wenn MS C# so weit erweitern würde, dass man das auf ähnlich einfache Weise umsetzen könnte, wie ich es im Pseudo-Code dargelegt habe.
 
Zuletzt bearbeitet:
Also mit so wenig Code wie du es vor hast geht es einfach nicht. Ein bisschen was muss man schon noch selbst machen beim programmieren :D
Ich würde entweder die Lösung von Grantig oder mir nehmen. Die sind beide nicht sehr komplex, sehr kurz und gut zu verstehen.
Wenn du es im setter machst kommst du auch nicht auf weniger Code würde ich mal behaupten.
Aber du kannst gerne deine Lösung hin schreiben, wie du es machen würdest.
Ich bin immer offen für andere Lösungswege.

P.S. Extension Methods benutze ich auch sehr gerne. Ist eine schöne und elegante Lösung.
 
Zurück
Oben