C# Multi Value Dictionary

roker002

Commander
Registriert
Dez. 2007
Beiträge
2.061
Ich versuche einen Multi Value Dictionary zu schreiben. Ich habe jetzt einen Code für Multi Key Dictionary gefunden. Eigentlich ist Multi Value Dict nichts anderes.

Meine Frage wäre... was macht
Code:
		public V this[L subKey]
{
	get
	{
		V item;
		if (TryGetValue(subKey, out item))
			return item;

		throw new KeyNotFoundException("sub key not found: " + subKey.ToString());
	}
}

public [COLOR="Red"]new[/COLOR] V this[K primaryKey]
{
	get
	{
		V item;
		if (TryGetValue(primaryKey, out item))
			return item;

		throw new KeyNotFoundException("primary key not found: " + primaryKey.ToString());
	}
}

New in dem codeausschnitt?

Der Sourcecode befindet sich HIER

Bin eigentlich in dem Bereich ganz neu... habe noch nie eine Indexer geschrieben.
 
Ups, verlesen. :$
 
Zuletzt bearbeitet: (Verlesen)
Das "new" überschreibt eine bestehende Methode/Eigenschaft der Basis-Klasse.

Siehe hier.


Update: Ah: Ninja'd :)
 
Multivalue Dictionary heißt jetzt nur, das du einen Key mehreren Values zuweisen willst?
Wenn ja, was spricht denn gegen Dictionary<Key,List<Value>> oder versteh ich was falsch?
 
Aber wenn deine beiden Values nicht vom gleichen Typ sind, kannst du die List<T> vergessen, außer du nimmst ne List<object> was unschön ist, da man dann andauernd casten muss.
 
Ok dann ist die bezeichnung meines Erachtens etwas verwirrend, sollte dann wohl eher MultiValueTyped Dictionary heißen, das man weiß es geht um die Typen. Gut verstehe.
 
Es geht wirklich um einen Key und 2 Values. Bei den Typen ist es egal was es sein sollte.

ahh jetzt ist klar! Danke für die antworten! Ich werde mal den Quellcode Posten sobald der fertig ist! kann aber net garantieren dass da alles einwandfrei läuft!
 
Wobei mir grad einfällt, dass man auch einfach ein Dictionary<T, KeyValuePair<TKey,TValue>> benutzen könnte.
Oder du schreibst ne eigene generische Klasse, so in der Art:

Code:
public class Values<TVal1,TVal2>
{
    public TVal1 Value1{get;set;}
    public TVal2 Value2{get;set;}

    public Values()
    {
    }

    public Values(TVal1 value1, TVal2 value2)
    {
        this.Value1 = value1;
        this.Value2 = value2;   
    }  
}

Verwendung:
Dictionary<int, Values<double, string>> dict = new Dictionary<int, Values<double, string>>();

dict.Add(1, new Values<double,string>(1, "eins"));
 
hmm die verwaltung durch multi Value Dictionary ist aber größer als ich dachte, leider! Ich denke ich werde einfach 2 Dicts verwenden und nach der Selbe ID dann abfragen. Auf jeden falls ist es im endeffekt leichter!

weiss jemand ob man die größe eine Dictionary komplett begrenzen kann. Mann kann ja im contructor die größe übergeben, aber im endeffekt lässt sich diese nachher so aufblassen wie man will.
 
Zuletzt bearbeitet:
roker002 schrieb:
hmm die verwaltung durch multi Value Dictionary ist aber größer als ich dachte, leider! Ich denke ich werde einfach 2 Dicts verwenden und nach der Selbe ID dann abfragen. Auf jeden falls ist es im endeffekt leichter!
Was spricht gegen die von "Grantig" vorgeschlagene Lösung? Das ist meiner Meinung nach die einfachste. Falls du für .Net 4 programmierst, kannst du sogar die mitgelieferte Tuple-Klasse nehmen.
weiss jemand ob man die größe eine Dictionary komplett begrenzen kann. Mann kann ja im contructor die größe übergeben, aber im endeffekt lässt sich diese nachher so aufblassen wie man will.
Das musst du dann wohl vor jedem Einfügevorgang prüfen.
 
roker002 schrieb:
hmm die verwaltung durch multi Value Dictionary ist aber größer als ich dachte, leider!
Eigentlich ist das doch ganz einfach, vor allem wenn man .NET 4 benutzt und dadurch Tuple verwenden kann.
Hab das ganze mal für 2 Values implementeirt + eine SizeLimit Property:
PHP:
public class DoubleValueDictionary<TKey, TValue1, TValue2> : Dictionary<TKey, Tuple<TValue1, TValue2>>
{
    #region Private Fields

    int m_sizeLimit;

    #endregion

    #region Properties

    /// <summary>
    /// The maximum number of entries in the dictionary. 0 means no limitation.
    /// </summary>
    public int SizeLimit
    {
        get { return m_sizeLimit; }
        set
        {
            if (value < 0)
                throw new ArgumentOutOfRangeException("The size limit must be equal to or higher than 0");

            if ((value != 0) && (value < this.Count))
                throw new ArgumentOutOfRangeException("The size limit must be 0 or equal to or higher than the current dictionary count.");

            m_sizeLimit = value;
        }
    }

    #endregion

    #region Public Methods

    public void Add(TKey key, TValue1 value1, TValue2 value2)
    {       
        this.Add(key, new Tuple<TValue1, TValue2>(value1, value2));
    }

    public new void Add(TKey key, Tuple<TValue1, TValue2> values)
    {
        if ((m_sizeLimit != 0) && (this.Count >= m_sizeLimit))
            throw new InvalidOperationException("The dictionary count may not be higher than the size limit.");

        base.Add(key, values);
    }

    #endregion
}
 
Zuletzt bearbeitet: (Überflüssigen Code entfernt)
eine andere frage die aber bezüglich dieses Themas ist. Wie kann ich einen Ereignis Auslösen der mir berichten soll dass es neue Eingaben (neuer Schlüssel und Value) gemacht worden sind.

z.B. wenn ich im Indexer selbst einen Ereignis auslösen will. Im Indexer selbst kann ich ja nach

Code:
public new void Add(TKey, Tuple<TValue1, TValue2>){}

Verweisen um dort einen Ereignis auszulösen.

Code:
public delegate void EntryChanged();

public event EntryChanged Changed;

muss ich dann im Constructor diesen Ereignis Initialisieren?
Ich bin irgendwie mit dem Bauen der Ereignisse überfordert.

habs bei MSDN gelesen aber kapiere immer noch nicht ganz wie genau das aufgerufen wird.
 
Den Delegaten "EntryChanged" brauchst du nicht, da es schon einen "EventHandler" Delegaten im Framework gibt.

Das Event deklarierst du dann so:
Code:
public event EventHandler Changed;

Dann definierst du eine Methode die du immer aufrufst, wenn das "Changed"-Event ausgelöst werden soll.
Code:
protected virtual void OnChanged(EventArgs e)
{
    EventHandler handler = this.Changed;
    if (handler != null)
    {
        handler(this, e);
    }
}
Wenn du keine besonderen EventArgs übergeben willst, rufst du die Methode einfach auf mit
Code:
OnChanged(EventArgs.Empty)

Das ganze sieht vielleicht etwas kompliziert aus, ist aber eine Konvention an die man sich halten sollte.
 
Ich habe nur das Problem... wenn ich "handler(this, e)" aufrufe... was passiert dann?
Ja klar irgendwo wird dann intern Weitergeleitet. Wird das delegate wieder auf null gesetzt sobald die benachrichtigung raus ist? das was oben steht ist klar. aber ohne tiefen verständis kann man sich das ja nicht so einfach einprägen!
 
Du musst natürlich in der Klasse in der du das Event behandeln möchtest, einen EventHandler "anhängen":
Code:
public class DictionaryUsage
{
    private DeinDictionary<DeinTyp> dict = new DeinDictionary<DeinTyp>();

    public DictionaryUsage()
    {
        this.dict.Changed += new EventHandler(this.DictChanged);
    }

    private void DictChanged(object sender, EventArgs e)
    {
        // Tu was
    }
}
Dann wird immer die Methode "DictChanged" aufgerufen, wenn das Event ausgelöst wird.
 
naja ich schreibe mal einfach den code rein!
Ich habe einen Extended Dictionary geschrieben (nicht Multivalue).
Code:
namespace MyDict
{
    public delegate void EntryChanged(Object sender, DictionaryChanged e);

    public class ExtDictionary<TKey, TValue> : Dictionary<TKey, TValue>
    {
        /// <summary>
        /// Die zuletzt benutzter Key und Value
        /// </summary>
        private KeyValuePair<TKey, TValue> LastUsed;

        /// <summary>
        /// Legt die Maximal länge des Dictionaries fest.
        /// Länge von 0 bedeutet dass es keine Längeneinschränkung gibt. 
        /// </summary>
        private uint MaxLen
        {
            get;
            set;
        }

        /// <summary>
        /// [COLOR="Red"]Eventhandler, der beim ändern der Werte[/COLOR] 
        /// [COLOR="Red"]in der Dictionary eine Benachrichtung aussenden kann.[/COLOR]
        /// </summary>
        public event EntryChanged Changed;

        /// <summary>
        /// Überprüft ob die Value exisiert. Gibt diese auch als out zurück.
        /// </summary>
        /// <param name="key">Der gesuchter schlüssel</param>
        /// <param name="val">Wertparameter der einen Schlüssel zugewiesen wurde</param>
        /// <returns>True wenn es den Key gibt.</returns>
        public new Boolean TryGetValue(TKey key, out TValue val)
        {
            val = default(TValue);

            try
            {
                if (base.TryGetValue(key, out val))
                    return true;
            }
            catch
            { }

            return false;
        }

        /// <summary>
        /// Ruft den Wert ab der dem angegebenen Schlüssel ist oder legt diesem fest.
        /// </summary>
        /// <param name="key">Der Schlüssel des abrufenden oder festlegenden Wertes.</param>
        /// <returns>Wert der den Schlüssel zugewiesen wurde.</returns>
        public new TValue this[TKey key]
        {
            get
            {
                TValue val;
                if (TryGetValue(key, out val))
                    return val;

                throw new KeyNotFoundException("Offenbar existiert der Schlüssel " + key.ToString() + " nicht.");
            }
            set
            {
                try
                {
                    this.Add(key, value);
                }
                catch
                {
                    throw new ArgumentException("Es konnte kein Eintrag in der ExtDictionary erstellt werden.");
                }
            }
        }

        /// <summary>
        /// Überschriebene Methode zur einfügung des Parameters.
        /// Hierbei muss man beachten dass es ein Indexer für das Schreiben
        /// der Werte eingesetzt wird und nicht die "base.Add" Methode.
        /// </summary>
        /// <param name="key">Schlüssel der Dictionary.</param>
        /// <param name="val">Wert</param>
        public new void Add(TKey key, TValue val)
        {
            if (MaxLen != 0 && (base.Count + 1) > MaxLen)
                throw new ArgumentOutOfRangeException("ExtDictionary", "Maximale Länge des Dictionary wurde erreicht.\r\nKein neuer Wert zum einfügen möglich.");

            base[key] = val;
            LastUsed = new KeyValuePair<TKey, TValue>(key, val);
            [COLOR="Red"]OnChanged();[/COLOR]

            ///Reset Key und Value Werte
            LastUsed = new KeyValuePair<TKey, TValue>();
        }

        /// <summary>
        /// Löst ein Eventhandler aus.
        /// </summary>
        [COLOR="Red"]public virtual void OnChanged()[/COLOR]
        {
            ///Eigene Klasse für diesen Event dass vom EventArgs erbt.
            DictionaryChanged e = new DictionaryChanged();
            e.Key = LastUsed.Key;
            e.Value = LastUsed.Value;

            if(Changed != null)
               [COLOR="Red"] Changed(this, e);[/COLOR] //<-- Hier Löse ich ereignis aus!
        }

        /// <summary>
        /// Erweiterter Dictionary dass auch einen Eventhandler 
        /// hat der die benachrichtigung übernimmt, falls es ein 
        /// Wert hinzugefügt/geändert wurde.
        /// </summary>
        /// <param name="len">Maximal erlaubte Länge des Dictionary.</param>
        public ExtDictionary(uint max)
        {
            this.MaxLen = max;
        }

        /// <summary>
        /// Erweiterter Dictionary dass auch einen Eventhandler 
        /// hat der die benachrichtigung übernimmt, falls es ein 
        /// Wert hinzugefügt/geändert wurde.
        /// </summary>
        public ExtDictionary()
        {
            this.MaxLen = 0;
        }
    }
}

Was genau passiert bei "Changed(this, e)" Sobald dieser Ereignis vorbei ist... wird dieser delegate auf null zurückgesetzt?
 
Wenn du "Changed(this, e)" aufrufst werden die Methoden aufgerufen, die du wie in meinem letzten Post beschrieben habe mit dem "+="-Operator registriert hast.
Der Delegat wird nicht auf null gesetzt. Du kannst aber einen EventHandler analog zum "anhängen" mit dem "-="-Operator auch wieder entfernen.
 
Zurück
Oben