2016-04-20 1 views
0

Ich schreibe eine Funktion, die irgendeine Art von Eingabedatenstruktur benötigt (wahrscheinlich eine DataTable; sie liegt immer noch in der Luft, aber der wichtige Teil ist, dass ihre Struktur gewonnen hat) t bis zur Laufzeit definiert sein und eine seiner String-Spalten in Token-Listen aufteilen. Das Endergebnis wird eine Datenstruktur sein (möglicherweise ein Dictionary, aber es liegt immer noch in der Luft), die einen schnellen Abruf dieser Tokenlisten basierend auf dem Primärschlüssel aus der ursprünglichen Datenstruktur ermöglicht.Wörterbuch mit zusammengesetztem Schlüssel, der zur Kompilierungszeit nicht definiert ist

Normalerweise würden Sie ein Tuple für einen zusammengesetzten Schlüssel verwenden, aber da dies ein Werkzeug sein soll, das eine Verbindung zu einer Datenbank herstellt und beliebige Tabellen/Abfragen abruft, kann ich diesen Ansatz hier nicht verwenden. Gibt es etwas, das dafür geeignet ist, das in .NET integriert ist, bevor ich meine eigene CompoundKey-Klasse schreibe, um damit umzugehen? Es ist etwas, das an mehreren Stellen in dieser Anwendung auftauchen wird, nicht nur diese spezielle Tokenisierungsfunktion.

Hier ist eine sehr grobe Vorstellung davon, wie die Methode aussehen würde, wobei "object" für welche Klasse auch immer Schlüsselklasse verwendet wird. Beachten Sie, dass die Klasse CompoundKey (noch) nicht existiert und WordBreakChars entweder string[] oder char[] sind, die an anderer Stelle in der Klasse vorbereitet wurden.

public Dictionary<object, string[]> SplitTokens(DataTable table, string split_column) { 
    Dictionary<object, string[]> Results = new Dictionary<object, string[]>(); 
    DataColumn[] KeyCols = table.PrimaryKey; 
    if (KeyCols == null || KeyCols.Length == 0) { 
     throw new ArgumentException("DataTable has no primary key."); 
    } 

    foreach (DataRow row in table.Rows) { 
     string[] tokens = (row[split_column] as string ?? "").Split(WordBreakChars, StringSplitOptions.RemoveEmptyEntries); 
     CompoundKey key = new CompoundKey(); 
     foreach (DataColumn col in KeyCols) { 
      key.Add(col.ColumnName, row[col]); 
      Results.Add(key, tokens); 
     } 
    } 

    return Results; 
} 
+0

Nun, Verwendung Objekt. Jedes tatsächliche Objekt, das Sie zur Laufzeit verwenden, muss GetHashCode() und Equals() korrekt implementieren. Wie du das machst, liegt ganz bei dir. –

+0

@HansPassant Ja, ich werde wahrscheinlich am Ende das Dictionary einpacken/erweitern und ein paar Verbesserungen vornehmen, um es für den Gleichheitsvergleich geeigneter zu machen. Ich will das einfach nicht machen, wenn .NET hier schon eine elegante und getestete Lösung bietet. Zeit, ILSpy herauszuholen und durch das Dictionary zu schlendern, um zu sehen, wie es GetHashCode() und Equals() sofort implementiert. – db2

Antwort

0

In Situationen wie diesem würde ich eine generische Funktion mit einem "Key Selector" machen. In der Tat bietet LINQ dies bereits in einem der es Überlastungen von .ToDictionary( Sie, dass in Verbindung mit Ihrem vorhandenen Code verwenden können, um das Verfahren erheblich zu vereinfachen

public Dictionary<T, string[]> SplitTokens<T>(DataTable table, string split_column, Func<DataRow, T> keySelector) 
{ 
    Dictionary<T, string[]> Results; 

    Results = table.AsEnumerable().ToDictionary(keySelector, 
       row => (row[split_column] as string ?? "").Split(WordBreakChars, StringSplitOptions.RemoveEmptyEntries)); 

    return Results; 
} 

Nun kann der Anrufer übergeben, was sie wollen, um keySelector und das wird sei der stark typisierte Datentyp für das resultierende Wörterbuch.

+0

Ich mag es, aber es scheint, als würde es noch eine Kompilierzeit-Anforderung geben, um den Typ/die Struktur des Schlüssels * irgendwo * zu kennen. Es ist nur, dass es jetzt in dem ist, was 'SplitTokens()' aufruft. In dieser Anwendung wird der Typ des Schlüssels erst zur Laufzeit bekannt sein, da er von jeder vom Benutzer ausgewählten Tabelle/Abfrage abgeleitet wird, die schließlich für die Tokenisierung übergeben wird. – db2

0

Das ist nicht besonders schick, aber es scheint, dass es den Job machen wird.

public class CompoundKey : IEquatable<CompoundKey>, IEnumerable { 
    private object[] _Key; 
    private int _Hash; 
    private bool _Hashed; 

    //Dictionary keys need to be immutable. DO NOT expect sane behavior if you modify items inside the key. 
    public object this[int index] { 
     get { return _Key[index]; } 
    } 

    public CompoundKey(params object[] Key) { 
     _Key = Key; 
     _Hashed = false; 
    } 

    public static implicit operator CompoundKey(object[] Key) { 
     return new CompoundKey(Key); 
    } 

    public int Length { get { return _Key.Length; } } 

    public override int GetHashCode() { 
     if (!_Hashed) { 
      _Hash = 0; 
      foreach (object o in _Key) { 
       if (o != null) { 
        _Hash ^= o.GetHashCode(); 
       } 
      } 
     } 
     return _Hash; 
    } 

    public bool Equals(CompoundKey other) { 
     if (other.GetHashCode() != _Hash) { 
      return false; 
     } 
     if (other.Length != this.Length) { 
      return false; 
     } 
     for (int i = 0; i < this.Length; i++) { 
      if (other[i] != this[i]) { 
       return false; 
      } 
     } 

     return true; 
    } 

    public override bool Equals(object obj) { 
     if (!(obj is CompoundKey)) { 
      return false; 
     } 
     return this.Equals((CompoundKey)obj); 
    } 

    public static bool operator ==(CompoundKey a, CompoundKey b) { 
     if ((object)a == null || (object)b == null) { 
      return false; 
     } 
     return a.Equals(b); 
    } 

    public static bool operator !=(CompoundKey a, CompoundKey b) { 
     return !(a == b); 
    } 

    public IEnumerator GetEnumerator() { 
     return _Key.GetEnumerator(); 
    } 
} 

fügte ich die implizite Umwandlung von object[], so die Nutzung als Einspeisen eines object[] direkt in das Wörterbuch vereinfacht werden kann. (Ich weiß, ich kann für Schleife der innere Linq weg, die das Tastenfeld mit .Select() bauen, ich es einfach gemacht etwas deutlicher für jetzt einfache Fehlersuche.)

public Dictionary<CompoundKey, string[]> SplitTokens(DataTable table, string split_column) { 
     Dictionary<CompoundKey, string[]> Results = new Dictionary<CompoundKey, string[]>(); 
     DataColumn[] key = table.PrimaryKey; 
     Regex RemoveIgnoredCharacters = new Regex("[" + Regex.Escape(Ignore) + "]"); 

     char[] WordBreakChars = WordBreak.ToCharArray(); 

     for (int i = 0; i < table.Rows.Count; i++) { 
      string split_value = RemoveIgnoredCharacters.Replace(table.Rows[i][split_column] as string ?? "", ""); 
      string[] tokens = split_value.Split(WordBreakChars, StringSplitOptions.RemoveEmptyEntries); 
      object[] dictkey = new object[key.Length]; 
      for (int j = 0; j < key.Length; j++) { 
       dictkey[j] = table.Rows[i][key[j].ColumnName]; 
      } 
      Results.Add(dictkey, tokens); 
     } 

     return Results; 
    }