2012-10-17 18 views
6

Ich habe ein Types-Projekt, in dem ich benutzerdefinierte Klassenobjekte definiere, an denen ich in meiner Hauptanwendung arbeiten möchte. Die Objekte werden grundsätzlich aus Strings abgeleitet und in eine Struktur zerlegt.Wie können mehrere benutzerdefinierte Typen unterstützt werden?

Ich habe zwei Probleme

1 - In einem separaten Projekt, das ich ein File-Reader-Klasse, wo ich Textdateien für die Abkürzungen scannen I definiert haben. Zum Beispiel durch regulären Ausdruck. Zur Zeit habe ich mein Types-Projekt als Projektreferenz hinzugefügt und listet die regulären Ausdrücke am Anfang meiner Leseklasse auf. Wenn ich einen Typ finde, wandle ich die Zeichenkette in den passenden Typ um. Wie kann ich das verbessern, so dass es direkt mit meinem Typ-Projekt verbunden ist - wenn ich es mit neuen Typen aktualisiere, weiß die Klasse Lesen, dass sie die neuen Typen unterstützen soll?

2 - Ich versuche, eine DLL zu erstellen, die auf diese spezifischen Typen funktioniert, nachdem sie aus der Textdatei gelesen werden. Wie kann ich meiner DLL mitteilen, dass ich die Typen in meinem Typ-Projekt unterstützen möchte? Muss ich für jeden Typ, an dem ich arbeiten möchte, eine überladene Funktion erstellen? Benütze ich eine Schnittstelle?

Jeder Rat wird sehr geschätzt.

EDIT: Hinzugefügt Beispielcode von dem, was I '' m versuchen

zu tun

// PROJEKT 1 - Griffe IO-Betrieb wie Lesen und Schreiben
// Funktion in Job Lese Klasse ist eine von zu finden mehrere vordefinierte String-Typen von regulärem Ausdruck ... einmal fanden sie in die Datenstruktur umgewandelt werden (durch in dem anderen Projekt definiert Zeichenfolge Konstruktor vom Typ Klasse vorbei

public class Read 
{ 
    public string[] FileList { get; set; } 

    private static Int64 endOffset = 0; 
    private FileStream readStream; 
    private StreamReader sr; 

    private System.Text.RegularExpressions.Regex type1 = new System.Text.RegularExpressions.Regex(@"@123:test"); 
    private System.Text.RegularExpressions.Regex type2 = new System.Text.RegularExpressions.Regex(@"TESTTYPE2"); 

    public Read(string[] fl) 
    { 
     FileList = fl; 
    } 

    public object ReturnMessage(FileStream readStream, out int x) 
    { 
     //readStream = new FileStream(file, FileMode.Open, FileAccess.Read); 
     x = 0; 
     //endOffset = 0; 
     bool found = false; 
     char ch; 
     string line = string.Empty; 

     object message = null; 

     while (!(x < 0)) //do this while not end of line (x = -1) 
     { 
      readStream.Position = endOffset; 

      //line reader 
      while (found == false) //keep reading characters until end of line found 
      { 
       x = readStream.ReadByte(); 
       if (x < 0) 
       { 
        found = true; 
        break; 
       } 
       // else if ((x == 10) || (x == 13)) 
       if ((x == 10) || (x == 13)) 
       { 
        ch = System.Convert.ToChar(x); 
        line = line + ch; 
        x = readStream.ReadByte(); 
        if ((x == 10) || (x == 13)) 
        { 
         ch = System.Convert.ToChar(x); 
         line = line + ch; 
         found = true; 
        } 
        else 
        { 
         if (x != 10 && (x != 13)) 
         { 
          readStream.Position--; 
         } 
         found = true; 
        } 
       } 
       else 
       { 
        ch = System.Convert.ToChar(x); 
        line = line + ch; 
       } 
      }//while - end line reader 



      //examine line (is it one of the supported types?) 
      if (type1.IsMatch(line)) 
      { 
       message = line; 
       endOffset = readStream.Position; 

       break; 
      } 
      else 
      { 
       endOffset = readStream.Position; 
       found = false; 
       line = string.Empty; 
      } 

     }//while not end of line 


     return message; 
    } 

} 

// PROJECT 2 - enthält Klassen, die definieren Typen

// TYP1

namespace MessageTypes.Type1 
{ 
public sealed class Type1 
{ 
    public List<Part> S2 { get; set; } 

    public Type1(string s) 
    { 
     S2 = new List<Part>(); 
     string[] parts = s.Split(':'); 
     for (int i = 0; i < parts.Length; i++) 
     { 
      S2.Add(new Part(parts[i])); 
     } 
    } 
} 

public sealed class Part 
{ 
    public string P { get; set; } 

    public Part(string s) 
    { 
     P = s; 
    } 
} 
} 

// TYPE 2

namespace MessageTypes.Type2 
{ 
public sealed class FullString 
{ 
    public string FS { get; set; } 

    public FullString(string s) 
    { 
     FS = s; 
    } 
} 
} 

// PROJECT 3

class DoSomethingToTypeObject{ 

//detect type and call appropriate function to process 

} 

// Projekt 4 - Hauptprojekt mit GUI

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
    } 
    private void button1_Click(object sender, RoutedEventArgs e) 
    { 
     if (tabControl1.SelectedIndex == 0) //Processing Mode 1 
     { 
      //load file list from main window - Mode1 tab 
      IOHandler.Read read = new IOHandler.Read(new string[2] { @"C:\file1.txt", @"C:\file2.txt" }); 

      //read files 
      foreach (string file in read.FileList) 
      { 

       //while not end of stream 
       myobject = read.ProcessFile(file); 

       DoSomethingtoTypeObject DS = new DoSomethingtoTypeObject(myobject); 

       //write transoformed object 
       write(myobject); 
      } 
     } 
    } 
} 

Antwort

1

Ich hoffe, ich verstehe Sie richtig.

Die Klasse, die Sie im Type-Projekt erstellen, stellt einige Objekte mit unterschiedlichen Verhalten, aber denselben Datenelementen dar, und Sie möchten diese problemlos in Ihren Projekten verwenden können, ohne diese Objekte explizit auflisten zu müssen.

Ich würde einige Basis-Schnittstelle erstellen, die alle meine Objekte im Projekt Typen implementieren würde. Ich würde dann eine Factory-Klasse verwenden, die Reflektion verwenden würde, um alle Objekte zu sammeln, die diese Schnittstelle implementieren.

public interface iFoo 
{ 
    string FoundItem { get; set; } 
    string Expression { get; } 
    string Value { get; set; } 
    void sharedFunctionName(); 
} 

public static class FooFactory 
{ 
    public static List<iFoo> GetTypeList() 
    { 
     List<iFoo> types = new List<iFoo>(); 
     types.AddRange(from assembly in AppDomain.CurrentDomain.GetAssemblies() 
         from t in assembly.GetTypes() 
         where t.IsClass && t.GetInterfaces().Contains(typeof(iFoo)) 
         select Activator.CreateInstance(t) as iFoo); 

     return types; 
    } 
} 

Dann würde Ihr Reader alle notwendigen Informationen für die unterstützten Typen erhalten, ohne dass Sie es manuell diktieren müssen.

Da ich der Werttyp denke irgendwann anders wäre, könnten Sie eine Generic Interface wie folgt verwenden:

public interface iFoo 
{ 
    string FoundItem { get; set; } 
    string Expression { get; } 
    void sharedFunctionName(); 
} 

public interface iFoo<T> : iFoo 
{ 
    T Value { get; set; } 
} 

public static class FooFactory 
{ 
    public static List<iFoo> GetTypeList() 
    { 
     List<iFoo> types = new List<iFoo>(); 
     types.AddRange(from assembly in AppDomain.CurrentDomain.GetAssemblies() 
         from t in assembly.GetTypes() 
         where t.IsClass && t.GetInterfaces().Contains(typeof(iFoo)) 
         select Activator.CreateInstance(t) as iFoo); 

     return types; 
    } 
} 

public class FooBar : iFoo<int> 
{ 

} 

In diesem Beispiel ist die Basis-Schnittstelle IFoo gehalten wird, um den Erkennungsprozess zu erleichtern. Die Verwendung der generischen Schnittstelle würde es erlauben, Ihren Code-Typ sicher zu halten (im Gegensatz zur Verwendung eines Objekts vom Typ Wert), aber Sie müssen etwas Logik hinzufügen, wenn Sie Ihre Objekte wiederherstellen, um auf Ihren Wert richtig zugreifen zu können.

Außerdem, wenn Sie jemals Funktionen erstellen müssen, die in allen Ihren Objekten geteilt werden müssten, könnten Sie Erweiterungsmethoden innerhalb der Factory-Klasse und Voilà hinzufügen.

EDIT:

auf den neuen Informationen Basierend:

Ihre Typen zu einer Art von Daten entsprechen, die Sie in einer Datei auf einem gewissen regulären Ausdruck basiert finden. Je nach Benutzerauswahl und Typ gibt es möglicherweise eine andere Art der Umwandlung.

Wir wissen, dass der Benutzer einen Modus aus einer Liste auswählen muss. Dies wirkt sich auf die Transformation aus, die auf die Typen angewendet werden soll.

Also das ist was ich tun würde: Ich würde die Transformationslogik direkt in die Type-Klasse verschieben, Polymorphismus wird sich genau darum kümmern, welche Transformation aufgerufen wird.

Ich würde die RegularExpression verwenden, um den Typ in den Typ selbst zu erkennen, dies ermöglicht es Ihnen, Reflexion zu verwenden und die Factory-Klasse früher diskutieren leichter.

So ist alles Standard. Ihr Leser erkennt jeden neuen Typ, den Sie im Typ-Projekt ohne manuelle Intervention erstellen, und sobald festgestellt wird, dass die richtige Transformation angewendet werden kann und der ursprüngliche String immer zugänglich ist.

public enum UserMode {Mode1, Mode2}; 

public interface iType 
{ 
    string Expression {get;} 
    string OriginalString {get; set;} 
    string Transform(UserMode Mode); 
    iType getNewInstance(string OriginalString); 

} 

public class Type1 : iType 
{ 
    public string Expression {get { return "RegularExpression"; }} 
    public string OriginalString {get; set;} 
    //Add any other private members you need to accomplish your work here. 
    public string Transform(UserMode Mode) 
    { 
     switch(Mode) 
     { 
     case UserMode.Mode1: 
      //write the transformation code for this scenario 
      return ResultString; 
      break; 
     } 
    } 

    public iType getNewInstance(string Original) 
    { 
    return (iType)(new Type1(){ OriginalString = Original }); 
    } 
} 

public static class TypeFactory 
{ 
    public static List<iType> GetTypeList() 
    { 
     List<iType> types = new List<iType>(); 
     types.AddRange(from assembly in AppDomain.CurrentDomain.GetAssemblies() 
         from t in assembly.GetTypes() 
         where t.IsClass && t.GetInterfaces().Contains(typeof(iType)) 
         select Activator.CreateInstance(t) as iType); 

     return types; 
    } 
} 

nun alles, was Sie tun müssen, wenn Sie den Ausdruck in der Liste von dem iTypes entsprechen. Wenn Sie ein Spiel haben Sie tun:

var TransformationReady = from t in TypeFactory.GetTypeList() 
          where Regex.IsMatch(YourFileLine, t.Expression) 
          select t.getNewInstance(Regex.Match(YourFileLine, t.Expression)); 
+0

Ich werde das versuchen. Ich habe meinen ursprünglichen Beitrag mit einem Beispielcode für das, was ich versuche, aktualisiert. Bitte sehen Sie nach, ob sich Ihre Vorschläge ändern. Ich bin mir nicht sicher über diese ** 'String FoundItem {bekommen; einstellen; } Zeichenfolge Ausdruck {get; } '** in der Schnittstelle. Was ist FoundItem und Ausdruck (die Übereinstimmung und der reguläre Ausdruck?).Für mich ist es eine erste Saite, wenn ich sie finde, aber sie wird später in die Datenstruktur umgewandelt. Ist das wichtig? – sjs

+0

FoundItem und Expression waren nur Beispiele für Member in der Schnittstelle und müssen nicht dort sein. Ich sehe mir den Code an, den Sie angegeben haben, verwenden Sie nur das Ergebnis einer bestimmten Transformation basierend auf dem Typ oder müssen Sie diese Struktur verwenden, wie sie später ist? –

+0

Meine DoSomethingClass wird die Zeichenfolge innerhalb des Objekts ändern. Angenommen, mein String-Objekt repräsentiert eine Zeile aus einer CSV-Datei. Vielleicht muss ich die 3. Spalte durch einen bestimmten Wert ersetzen. In diesem Fall muss ich die gleiche Struktur zurückgeben, damit die Schreibfunktion Zugriff auf alle Werte hat, aus denen die Zeile besteht. Jede Implementierung von DoSomthing (pro Typ) behält die Struktur intakt. Ich habe möglicherweise andere Modi (DoSomething2), die das Objekt verwenden, aber verschiedene Funktionen wie Wert abrufen ausführen. Nur die Write-Klasse gibt eine Zeichenfolge so aus, wie sie ursprünglich in der Datei war. – sjs

6

Sie sollten eine Schnittstelle verwenden und dann alle Ihre Typen die Schnittstelle implementieren. Danach sollten Sie Ihre Read-Klasse ändern, um auf der Schnittstelle NICHT die einzelnen Klassen zu betreiben.

Auf diese Weise können Sie beliebig viele Typen hinzufügen und müssen die Klasse Read nicht aktualisieren.

+1

Dies würde nur funktionieren, wenn Sie vor der Zeit wissen, welche Art Sie als nächstes zu lesen erwartet haben. –

+0

Sind Sie sicher? Solange er die Schnittstelle und seine Klassen nutzt, die er jetzt hat und in der Zukunft diese Schnittstelle implementiert? – mattytommo

+1

Die Implementierung müsste über alle Typen für Erkennungszwecke wissen. Das scheint mir nicht viel SRP zu sein. Obwohl, ich nehme an, Sie könnten die Typen registrieren, die die Schnittstelle mit einer Art Fabrikobjekt implementieren. –

Verwandte Themen