2009-06-08 9 views
7

Mam klasy, które można przekazać w folderze, a następnie wyłączy się i przetwarza wiele danych w określonym folderze.C# Konstruktor Konstrukcja

Na przykład:

MyClass myClass = new MyClass(@"C:\temp"); 

co teraz robi to gaśnie i czyta powiedzieć parę tysięcy plików i zapełnia klasę z danymi.

powinienem przenieść te dane się od konstruktora i mieć go jako metody indywidualne, takie jak:

MyClass myClass = new MyClass(); 
myClass.LoadFromDirectory(@"C:\temp"); 

Odpowiedz

22

Może powinieneś spróbować w ten sposób ze statycznej metody, która zwraca instancję obiektu.

var myClass = MyClass.LoadFromDirectory(@"C:\temp"); 

Spowoduje to zachowanie kodu inicjalizacyjnego poza twoim konstruktorem, a także podanie deklaracji "jedna linia", której szukasz.


Idąc na komentarz od dołu z afisza, dodając członkowskiemu wdrożenie mogłoby być tak:

public class MyClass 
{ 

#region Constructors 

    public MyClass(string directory) 
    { 
     this.Directory = directory; 
    } 

#endregion 

#region Properties 

    public MyClassState State {get;private set;} 

    private string _directory; 

    public string Directory 
    { 
     get { return _directory;} 
     private set 
     { 
      _directory = value; 
      if (string.IsNullOrEmpty(value)) 
       this.State = MyClassState.Unknown; 
      else 
       this.State = MyClassState.Initialized; 
     } 
    } 

#endregion 



    public void LoadFromDirectory() 
    { 
     if (this.State != MyClassState.Initialized || this.State != MyClassState.Loaded) 
      throw new InvalidStateException(); 

     // Do loading 

     this.State = MyClassState.Loaded; 
    } 

} 

public class InvalidStateException : Exception {} 


public enum MyClassState 
{ 
    Unknown, 
    Initialized, 
    Loaded 
} 
+0

Dobry pomysł, inicjowanie i korzystanie z klasy są często zupełnie inne. To ładnie je oddziela. Aby uzyskać jeszcze większą separację, możesz przenieść logikę inicjalizacji do klasy fabryki lub konstruktora. – Mendelt

1

Czy to wszystko twoja klasa robi? Jeśli tak, to powiedziałbym, że to naprawdę nie ma znaczenia. Ale prawdopodobne jest, że jesteś klasą, która robi więcej niż to, co pokazałeś. Czy ma na przykład obsługę błędów?

Celem konstruktora jest skonstruowanie obiektu. Celem metody jest wykonanie działania. Więc mój głos jest dla tej formy:

MyClass myClass = new MyClass(); 
myClass.LoadFromDirectory(@"C:\temp"); 
+0

Będzie dużo więcej dla tej klasy i jestem w trakcie jej projektowania. Odwaga klasy wymaga jednak, aby została wypełniona danymi przekazanymi do klasy. –

5

To zależy. Powinieneś ocenić podstawowy cel zajęć. Jaką funkcję wykonuje?

To, co zwykle wolę, to mieć konstruktor klasy do inicjalizacji niezbędnej do funkcjonowania klasy. Następnie wywołuję metody na klasie, które mogą bezpiecznie założyć, że wykonano niezbędną inicjalizację.

Zazwyczaj faza inicjalizacji nie powinna być zbyt intensywna. Alternatywnym sposobem robi powyżej mogą być:

// Instantiate the class and get ready to load data from files. 
MyClass myClass = new MyClass(@"C:\temp"); 

// Parse the file collection and load necessary data. 
myClass.PopulateData(); 
+0

To jest rzeczywiście najlepsza praktyka i większość narzędzi do refaktoryzacji powie Ci, aby nie wywoływać metod od konstruktorów, chyba że są to metody inicjowania, takie jak tworzenie formantów lub ustawianie właściwości. –

+5

To prowadzi do tego, że klasa ma dwa bardzo wyraźne stany - jeden, w którym ma nazwę pliku, i jedną, w której zawiera prawdziwe dane. Po populacji wygląda na to, że w ogóle nie będzie potrzebna nazwa pliku, ale nadal będzie potrzebna do tego zmienna. Takie rzeczy zawsze mnie denerwują. Preferuję metodę statycznej metody, w której wynikiem jest obiekt, który jest "gotowy do działania". –

+0

@Jon Skeet, niesamowite, nie najlepsze miejsce, ale Twoja książka C# in Depth bardzo mi pomogła. Dzięki! - Moją obawą jest to, że tworzenie tej klasy może potrwać kilka minut, ponieważ przetwarza dane, a posiadanie podwójnego stanu jest tym, czego miałem nadzieję uniknąć. –

0

myślę, że należy zdecydować pomiędzy tymi dwoma podejściami powyżej ("najpierw zainicjować, a następnie wykonaj "vs" pusty init, wykonaj z parametrami ") na podstawie tego, czy planujesz ponowne użycie tego samego obiektu do wykonania tej samej operacji na innym wejściu.
jeśli klasa jest używana tylko do uruchomienia zadania na ustalonym parametrze, uruchomiłbym go w konstruktorze (czyniąc go tylko do odczytu), a następnie wykonując zadanie inną metodą.
Jeśli chcesz kontynuować wykonywanie zadania na różnych parametrach, umieściłbym go w samej metodzie zadania.

Jeśli cała klasa robi to zadanie, rozważyłbym również zmianę tego na statyczną klasę/metody - nie musi ona zachowywać swojego stanu wewnętrznego.

W każdym razie, nigdy nie umieszczę samego zadania w konstruktorze. Jak powiedział Cerebrus, inicjalizacja powinna przebiegać szybko.

0

O ile głównym celem zajęć nie jest wykonanie operacji wejścia/wyjścia, prawdopodobnie nie należy wykonywać operacji we/wy (potencjalnie rzucając wyjątek IOException) w konstruktorze.

rozważyć podział na dwie klasy:

interface IMyDataSource 
{ 
    // ... 
} 

class FileDataSource: IMyDataSource 
{ 
    public FileDataSource(string path) 
    { 
     // ... 
    } 
} 

class MyClass 
{ 
    public MyClass(IMyDataSource source) 
    { 
     // ... 
    } 
} 

IMyDataSource myDS = new FileDataSource(@"C:\temp"); 
MyClass myClass = new MyClass(myDS); 

ten sposób główny klasa może skupić się na zarządzaniu własnego państwa, a źródłem danych buduje stan początkowy od zawartości plików.

+1

Ale teraz FileDataSource ma potencjał wyrzucania wyjątków w jego konstruktorze, a więc tylko przekazywanie błędów do osobnego obiektu. –

+0

@ Tom Anderson, to jest idea. FileDataSource jest przeznaczony do wykonywania operacji we/wy, dlatego oczekuje się wyjątków we/wy w konstruktorze. MyClass nie jest zaprojektowany do wykonywania operacji we/wy, więc unikamy wyjątków we/wy w konstruktorze. – finnw

0

Jeśli jest to jedyny zasób, z którym pracuje klasa, prawdopodobnie lepiej przekazać ścieżkę do konstruktora. W przeciwnym razie byłby to parametr dla członków twojej klasy.

0

Moje osobiste preferencje to używanie inicjalizatorów C# 3.0.

class MyClass { 
    public string directory; 
    public void Load() { 
     // Load files from the current directory 
     } 
    } 

MyClass myClass = new MyClass{ directory = @"C:\temp" }; 
myClass.Load(); 

ten ma kilka zalet:

  • Object instancji nie będzie miał automatyczny plików efekt uboczny systemu.
  • Wszystkie argumenty są nazwane.
  • Wszystkie argumenty są opcjonalne (ale oczywiście mogłoby rzucić wyjątek w load(), jeśli nie określono)
  • można zainicjować jak wiele właściwości jako chcesz w zaproszeniu konkretyzacji bez przeciążenia konstruktor. Na przykład, opcje , czy rekurencyjne katalogi, lub wieloznaczny dla plikuspec do wyszukiwania dla.
  • Nadal można mieć pewną logikę w ustawieniach dla katalogu, aby zrobić kilka rzeczy, ale znowu efekty uboczne zwykle nie są dobre.
  • Wykonując operacje na plikach w oddzielnym wywołaniu procedury , unika się problemu polegającego na niemożności odniesienia się do instancji myClass w w obsługi wyjątku.
+0

Chociaż nadal nie jest to złe podejście, ma jednak stan, zestaw pól lub nie jest ustawiony. –

0

Zamierzam powtórzyć "podziel się nimi" tutaj. Jeśli to nie pomaga, spróbuj tego:

  1. Zadaj sobie pytanie: „Co robi ta metoda/nieruchomość/pole zrobić?”
  2. Zrób to; nie więcej nie mniej.

stosowania tego tutaj, to masz:

  1. Konstruktor ma stworzyć obiekt.
  2. Twoja metoda ma ładować swoje dane z systemu plików.

To wydaje mi się być o wiele bardziej logiczne niż „Konstruktor ma utworzyć obiekt i załadować dane z systemu plików.

+0

Uzgodnione. Jeśli chcesz zablokować i powrócić dopiero po zapełnieniu obiektu, użyj fabryki z metodą statyczną. – richardtallent

1

Zgadzam się z Ariem i innymi - podziel je.

Konstruktor powinien wykonać minimalną ilość pracy (po prostu zainicjuj obiekt gotowy do użycia i pozostaw to w tym miejscu). Używając osobnej metody do wykonania pracy:

  • Dla osoby dzwoniącej jest bardziej zrozumiałe, że funkcja pracownika może zająć dużo czasu.
  • Łatwo jest podać kilka konstruktorów, aby zainicjalizować obiekt różnymi informacjami (np. Możesz przekazać własną klasę (zamiast łańcucha), która może podać ścieżkę, lub możesz przekazać dodatkowy parametr, który Określa nazwę symbolu wieloznacznego, która ma być dopasowana, lub flagę określającą, czy wyszukiwanie ma się powtarzać w podfolderach.
  • Unikasz problemów z konstruktorem. W konstruktorze obiekt nie jest w pełni ukształtowany, więc wykonywanie pracy może być niebezpieczne - np. wywoływanie funkcji wirtualnej wewnątrz konstruktora jest bardzo złym pomysłem. Im mniej kodu wstawisz do konstruktora, tym mniej prawdopodobne, że zrobisz coś "złego" przez przypadek.
  • To czystszy styl kodowania, który rozdziela różne zachowania/funkcje na osobne metody. Zachowaj inicjalizację i oddziel je w pracy
  • Podział klasy będzie łatwiejszy do utrzymania i uzupełnienia w przyszłości.