2017-02-27 2 views
0

Ich habe eine Reihe von benutzerdefinierten Sammlung Klassen. Jedes dient dazu, eine Sammlung verschiedener benutzerdefinierter Typen bereitzustellen - einen benutzerdefinierten Typ für eine benutzerdefinierte Sammlung. Die benutzerdefinierten Sammlungen erben List<T> [wobei T in diesem Fall der spezifische benutzerdefinierte Typ und nicht ein generischer Typ ist] und bieten einige zusätzliche Funktionen.Klassen mit praktisch gemeinsamen Code

Ich habe zuvor die benutzerdefinierten Sammlungen entfernt und hatte benutzerdefinierte Methoden an anderer Stelle, aber ich fand, wie ich den Code erweiterte, dass ich die Sammlungen mit ihren eigenen Methoden brauchte.

Alles funktioniert, alles ist glücklich. Aber es irritiert mich, weil ich weiß, dass ich es nicht richtig mache. Das Problem ist, dass jede Klasse so ziemlich den gleichen Code verwendet und nur den Typ und einen Parameter variiert, so dass ich denke, dass sie als abstrakte Klasse oder als generische oder Erweiterung zu List implementiert werden kann, oder ... aber ich bin Ich verstehe nicht genug von den Unterschieden oder wie ich das machen kann, um herauszufinden, was ich brauche.

Hier sind zwei meiner mehreren Sammlungen, so dass Sie auf die Idee:

// JourneyPatterns 
public class JourneyPatterns : List<JourneyPattern> 
{ 
    private Dictionary<string, JourneyPattern> jpHashes;  // This is a hash table for quick lookup of a JP based on its values 

    /* Add a journey pattern to the JourneyPatterns collection. Three methods for adding: 
     1. "Insert Before" (=at) a particular point in the list. This is the method used by all three methods. 
     2. "Insert After" a particular point in the list. This is "before" shifted by 1 e.g. "after 6" is "before 7" 
     3. "Append" to the end of the list. This is "before" with a value equal to the list count, and is the same as inherited "Add", but with checks 
    */ 
    public JourneyPattern InsertBefore(JourneyPattern JP, int before) 
    { 
     // check for a pre-existing JP with the same parameters (ignore ID). Do this by constructing a "key" based on the values to check against 
     // and looking it up in the private hash dictionary 
     JourneyPattern existingJP; 
     if (jpHashes.TryGetValue(JP.hash, out existingJP)) { return existingJP; } 
     else 
     { 
      // construct a new ID for this JP 
      if (string.IsNullOrWhiteSpace(JP.id)) JP.id = "JP_" + (Count + 1).ToString(); 
      // next check that the ID specified isn't already being used by a different JPS 
      if (Exists(a => a.id == JP.id)) JP.id = "JP_" + (Count + 1).ToString(); 
      // now do the add/insert 
      if (before < 0) { Insert(0, JP); } else if (before >= Count) { Add(JP); } else { Insert(before, JP); } 
      // finally add to the hash table for fast compare/lookup 
      jpHashes.Add(JP.hash, JP); 
      return JP; 
     } 
    } 
    public JourneyPattern InsertAfter(JourneyPattern JP, int after) { return InsertBefore(JP, after + 1); } 
    public JourneyPattern Append(JourneyPattern JP) { return InsertBefore(JP, Count); } 
} 

// JourneyPatternSections 
public class JourneyPatternSections : List<JourneyPatternSection> 
{ 
    private Dictionary<string, JourneyPatternSection> jpsHashes;  // This is a hash table for quick lookup of a JPS based on its values 

    /* Add a journey pattern section to the journeyPatternSections collection. Three methods for adding: 
     1. "Insert Before" (=at) a particular point in the list. This is the method used by all three methods. 
     2. "Insert After" a particular point in the list. This is "before" shifted by 1 e.g. "after 6" is "before 7" 
     3. "Append" to the end of the list. This is "before" with a value equal to the list count, and is the same as inherited "Add", but with checks 
    */ 
    public JourneyPatternSection InsertBefore(JourneyPatternSection JPS, int before) 
    { 
     // check for a pre-existing JPS with the same parameters (ignore ID). Do this by constructing a "key" based on the values to check against 
     // and looking it up in the private hash dictionary 
     JourneyPatternSection existingJPS; 
     if (jpsHashes.TryGetValue(JPS.hash, out existingJPS)) { return existingJPS; } 
     else 
     { 
      // construct a new ID for this JPS 
      if (string.IsNullOrWhiteSpace(JPS.id)) JPS.id = "JPS_" + (Count + 1).ToString(); 
      // next check that the ID specified isn't already being used by a different JPS 
      if (Exists(a => a.id == JPS.id)) JPS.id = "JPS_" + (Count + 1).ToString(); 
      // now do the add/insert 
      if (before < 0) { Insert(0, JPS); } else if (before >= Count) { Add(JPS); } else { Insert(before, JPS); } 
      // finally add to the hash table for fast compare/lookup 
      jpsHashes.Add(JPS.hash, JPS); 
      return JPS; 
     } 
    } 
    public JourneyPatternSection InsertAfter(JourneyPatternSection JPS, int after) { return InsertBefore(JPS, after + 1); } 
    public JourneyPatternSection Append(JourneyPatternSection JPS) { return InsertBefore(JPS, Count); } 
} 

Wie Sie sehen können, was unterschiedlich ist, ist die Art (JourneyPattern oder JourneyPatternSection) und das Präfix, das ich bin Verwenden für die "ID" -Eigenschaft des Typs ("JP_" oder "JPS_"). Alles andere ist üblich, da die Methode zur Bestimmung der "Eindeutigkeit" (die Eigenschaft "Hash") Teil des benutzerdefinierten Typs ist.

Einige meiner benutzerdefinierten Sammlungen erfordern mehr involvierte und andere Implementierungen dieser Methoden, was in Ordnung ist, aber das ist die häufigste und ich habe es bisher etwa 6 Mal implementiert, was a) sinnlos und b) schwieriger scheint aufrecht erhalten.

Ihre Gedanken und Hilfe geschätzt!

+0

Machen Sie Ihre Klassen implementieren eine Schnittstelle, die enthalten s Eigenschaften "ID" und "Hash" und Typ auf diese Schnittstelle (Klasse YourCollection : Liste wo T: IYourInterface). – Evk

+0

OK, ich kann sehen, wie das funktionieren könnte. Vermutlich würde ich auch die benutzerdefinierten Typen benötigen, um auch eine andere Eigenschaft in der Schnittstelle zu implementieren, z. "id_pfx", so dass die Sammlung wusste, welches Präfix für die ID – StuartR143

+0

Yes verwendet werden soll, oder lassen Sie die Sammlung selbst Präfix speichern, wie die Antwort unten suggeriert. – Evk

Antwort

1

Assming tha sowohl JourneyPattern und JourneyPatternSection implementiert eine gemeinsame interface wie:

public interface IJourney 
{ 
    string hash { get; set; } 
    string id { get; set; } 
} 

Sie können eine Basisklasse für Ihre Sammlungen implementiert:

public abstract class SpecializedList<T> : List<T> where T : class, IJourney 
{ 
    private Dictionary<string, T> jpHashes;  // This is a hash table for quick lookup of a JP based on its values 

    protected abstract string IdPrefix { get; } 

    /* Add a journey pattern to the JourneyPatterns collection. Three methods for adding: 
      1. "Insert Before" (=at) a particular point in the list. This is the method used by all three methods. 
      2. "Insert After" a particular point in the list. This is "before" shifted by 1 e.g. "after 6" is "before 7" 
      3. "Append" to the end of the list. This is "before" with a value equal to the list count, and is the same as inherited "Add", but with checks 
    */ 
    public T InsertBefore(T JP, int before) 
    { 
     // check for a pre-existing JP with the same parameters (ignore ID). Do this by constructing a "key" based on the values to check against 
     // and looking it up in the private hash dictionary 
     T existingJP; 
     if (jpHashes.TryGetValue(JP.hash, out existingJP)) { return existingJP; } 
     else 
     { 
      // construct a new ID for this JP 
      if (string.IsNullOrWhiteSpace(JP.id)) JP.id = "JP_" + (Count + 1).ToString(); 
      // next check that the ID specified isn't already being used by a different JPS 
      if (Exists(a => a.id == JP.id)) JP.id = IdPrefix + (Count + 1).ToString(); 
      // now do the add/insert 
      if (before < 0) { Insert(0, JP); } else if (before >= Count) { Add(JP); } else { Insert(before, JP); } 
      // finally add to the hash table for fast compare/lookup 
      jpHashes.Add(JP.hash, JP); 
      return JP; 
     } 
    } 
    public T InsertAfter(T JP, int after) { return InsertBefore(JP, after + 1); } 
    public T Append(T JP) { return InsertBefore(JP, Count); } 
} 

dann jede Sammlung implementieren:

public class JourneyPatterns : SpecializedList<JourneyPattern> 
{ 
    protected override string IdPrefix => "JP_"; 
} 

public class JourneyPatternSections : SpecializedList<JourneyPatternSection> 
{ 
    protected override string IdPrefix => "JPS_"; 
} 
+0

Leider nicht. JourneyPatterns enthalten JourneyPatternSections (die JourneyPatternTimingLinks enthalten), aber es gibt keinen gemeinsamen Vorfahren. Aber danke - saubere Lösung, sonst. – StuartR143

+0

Sie können eine 'Schnittstelle' für beide anstelle eines gemeinsamen Vorfahren verwenden. –

+0

Aha! Danke für die Änderung, die die 'Interface'-Implementierung zeigt. Das ist exzellent - genau das, was ich brauche, und gibt mir ein gutes Beispiel für die Verwendung von Schnittstellen und Abstraktion. – StuartR143

Verwandte Themen