2012-10-17 14 views
6

Mam projekt Types, w którym definiuję niestandardowe obiekty klasy, które chcę pracować w mojej głównej aplikacji. Obiekty są w zasadzie wyprowadzane z łańcuchów i przetwarzane w strukturę.Jak obsługiwać wiele typów niestandardowych?

mam dwa problemy

1 - W osobnym projekcie mam klasy czytnika pliku, gdzie skanowanie plików tekstowych dla typów łańcuchowych Mam zdefiniowane. Na przykład przez wyrażenie regularne. Obecnie dodałem mój projekt Types jako odniesienie do projektu i po prostu wypisuję wyrazy regularne u góry mojej klasy odczytu. Kiedy znajdę typ, konwertuję ciąg znaków na odpowiedni typ. Jednak jak mogę to poprawić, aby był bezpośrednio połączony z moim typem projektu - więc kiedy aktualizuję go o nowe typy, klasa Read wie, że powinna obsługiwać nowe typy?

2 - Próbuję utworzyć bibliotekę DLL, która działa na tych określonych typach po ich odczytaniu z pliku tekstowego. Jak mogę poinformować moją bibliotekę DLL, że chcę obsługiwać typy w moim projekcie Types? Czy muszę wykonać przeciążoną funkcję dla każdego typu, nad którym chcę pracować? Czy korzystam z interfejsu?

Każda rada jest bardzo doceniana.

EDIT: Dodano przykład kodu, co I''m próbuje zrobić

// projekt 1 - uchwyty operacji IO jak czytanie i pisanie
// funkcję w klasie odczytu zadaniem jest znaleźć jedną z kilka predefiniowanych typów smyczkowych wyrażenia regularnego ... raz okazało się, że są konwertowane do struktury danych (przekazując ciąg do konstruktora klasy typu zdefiniowanego w innym projekcie

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; 
    } 

} 

// projekt 2 - zawiera klasy, które definiują typy

// TYPE1

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; 
    } 
} 
} 

// TYP 2

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

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

// projekt 3

class DoSomethingToTypeObject{ 

//detect type and call appropriate function to process 

} 

// projekt 4 - główny projekt z 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); 
      } 
     } 
    } 
} 

Odpowiedz

1

Mam nadzieję, że rozumiem cię poprawnie.

Klasa tworzona w projekcie Typ reprezentuje niektóre obiekty, które mają różne zachowania, ale te same elementy danych i użytkownik chciałby móc z nich korzystać w projektach bez konieczności wyraźnego wymieniania tych obiektów.

Utworzyłbym pewien bazowy interfejs, który implementowałyby wszystkie moje obiekty w projekcie Typy. Użyłbym wtedy klasy Factory, która użyłaby odbicia, aby zebrać wszystkie obiekty, które implementują wspomniany interfejs.

Wówczas Twój czytnik otrzyma wszystkie niezbędne informacje o obsługiwanych typach bez konieczności ręcznego dyktowania.

Od Chyba typ wartość byłaby inna, w pewnym momencie, można użyć interfejsu Generic tak:

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> 
{ 

} 

W tym przykładzie interfejs IFoo podstawa jest utrzymywana w celu ułatwienia procesu wykrywania. Użycie interfejsu generycznego pozwoliłoby zachować kod w bezpiecznym typie (w przeciwieństwie do użycia obiektu typu Wartość), ale przy odzyskiwaniu obiektów trzeba będzie dodać logikę, aby móc poprawnie uzyskać dostęp do wartości.

Co więcej, jeśli kiedykolwiek będziesz potrzebował stworzyć funkcje, które będą wymagały wymiany we wszystkich twoich obiektach, będziesz mógł dodać metody rozszerzenia w klasie Factory i Voila.

EDIT:

oparciu o nowe informacje:

Twoje typy odpowiadają rodzaj danych, które będzie można znaleźć w pliku opartego na jakimś wyrażeniu regularnym. Może istnieć inny rodzaj transformacji na podstawie wyboru użytkownika i typu.

Wiemy, że użytkownik będzie musiał wybrać tryb z listy, a to wpłynie na przekształcenie, które zostanie zastosowane w typach.

Oto, co powinienem zrobić: Przesunęłabym logikę transformacji wprost do klasy Type, polimofizm zajmie się dokładnie tym, która transformacja zostanie wywołana.

Chciałbym użyć funkcji RegularExpression do użycia, aby wykryć typ w samym typie, pozwoli to na łatwiejsze wykorzystanie refleksji i omówienie klasy Factory.

W ten sposób wszystko jest standardowe. Twój czytelnik zna dowolny nowy typ, który tworzysz w projekcie typu bez interwencji manualnej i po wykryciu, można zastosować odpowiednią transformację, a oryginalny ciąg jest zawsze dostępny.

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; 
    } 
} 

Teraz wszystko, co musisz zrobić, jeśli pasuje do wyrażenia z iTypes na liście. Kiedy masz zapałkę zrobić:

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

Spróbuję tego. Zaktualizowałem mój pierwotny wpis, podając przykładowy kod tego, co próbuję zrobić. Proszę spojrzeć na wypadek, gdyby zmieniło się którekolwiek z twoich sugestii. Nie jestem pewien co do tego ** 'string FoundItem {get; zestaw; } string Expression {get; } '** w interfejsie. co to jest founditem i wyrażenie (dopasowanie i wyrażenie regularne?).dla mnie jest to ciąg pierwszy, gdy go znajdę, ale później zostanie przekonwertowany na bazę danych. Czy to ma znaczenie? – sjs

+0

FoundItem i Expression, gdzie po prostu przykład członków w interfejsie i nie musi tam być. Sprawdzam podany kod, czy używasz wyniku określonej transformacji opartej na typie, czy też musisz użyć tej struktury w późniejszym czasie? –

+0

moja DoSomethingClass zmodyfikuje ciąg wewnątrz obiektu. Na przykład powiedz, że mój obiekt typu string reprezentuje linię z pliku CSV. Może muszę zastąpić trzecią kolumnę konkretną wartością. W tym przypadku muszę zwrócić tę samą strukturę, więc funkcja zapisu będzie miała dostęp do wszystkich wartości, które tworzą linię. Każda implementacja DoSomthing (według typu) zapewni nienaruszoną strukturę. Mogę mieć inne tryby (DoSomething2), które wykorzystują obiekt, ale wykonują inną funkcję, np. Pobierają wartość. Tylko klasa Write wypisze ciąg znaków tak jak był pierwotnie w pliku. – sjs

6

Powinieneś użyć interfejsu, a następnie sprawić, by wszystkie typy implementowały interfejs. Po wykonaniu tej czynności powinieneś zmienić swoją klasę Read, aby działała na interfejsie, a nie na poszczególnych klasach.

W ten sposób można dodać dowolną liczbę typów i nie trzeba aktualizować klasy Czytaj.

+1

ta będzie działać tylko wtedy, gdy wiedział z wyprzedzeniem, jaki rodzaj masz spodziewali się czytać dalej. –

+0

Jesteś tego pewien? Dopóki używa interfejsu i jego zajęć, które ma teraz i dodaje w przyszłości, implementuje ten interfejs? – mattytommo

+1

Implementacja musiałaby wiedzieć o wszystkich typach w celu detekcji. To nie wydaje mi się dużo SRP. Chociaż, przypuszczam, że mógłbyś zarejestrować typy, które implementują interfejs z jakimś rodzajem obiektu fabrycznego. –

Powiązane problemy