2010-07-08 11 views
7

Ho il seguente codice e vorrei scriverlo in modo che abbia linee di codice minime e il lavoro sia fatto allo stesso modo. Come lo posso fare?Come evitare la duplicazione del codice

List<Category> categoryList = new List<Category>(); 
categoryList = Category.LoadForProject(project.ID).ToList(); 
List<string> categories = new List<string>(Categories); 
IList<Category> currentCategories = Category.LoadForProject(project.ID).ToList(); 
if (currentCategories != null) 
{ 
    foreach (var existingCategories in currentCategories) 
    { 
     if (categories.Contains(existingCategories.Name)) 
      categories.Remove(existingCategories.Name); 
     else 
      existingCategories.Delete(Services.UserServices.User); 
    } 
    foreach (string item in categories) 
    { 
     Category category = new Category(project, item.ToString()); 
     category.Project = project; 
     category.Save(); 
    } 
} 

List<string> priorities = new List<string>(Priorities); 
IList<Priority> currentPriorities = Priority.LoadForProject(project.ID).ToList(); 
if (currentPriorities != null) 
{ 
    foreach (var existingPriorities in currentPriorities) 
    { 
     if (priorities.Contains(existingPriorities.Name)) 
      priorities.Remove(existingPriorities.Name); 
     else 
      existingPriorities.Delete(Services.UserServices.User); 
    } 
    foreach (string item in priorities) 
    { 
     Priority priority = new Priority(project, item.ToString()); 
     priority.Project = project; 
     priority.Save(); 
    } 
} 

risposta

9

Qualcosa del genere dovrebbe farlo:

public IList<T> DoYourThing<T>(IList<T> items, IList<T> currentItems, Project project) where T : CommonBaseType 
{ 
    if (currentItems != null) 
    { 
    foreach (var existingItem in currentItems) 
    { 
     if (items.Contains(existingItem.Name)) 
     items.Remove(existingItem.Name); 
     else 
     existingItems.Delete(Services.UserServices.User); 
    } 
    foreach (string item in items) 
    { 
     T newItem = Activator.CreateInstance(typeof(T), new object[] {project, item.ToString()}) as T; 
     newItem.Project = project; 
     newItem.Save(); 
    } 
    } 

    return currentItems; 
} 

Poi si può chiamare in questo modo:

var currentCategories = DoYourThing(Categories.ToList(), Category.LoadForProject(project.ID).ToList()); 
var currentProjects = DoYourThing(Priorities.ToList(), Priority.LoadForProject(project.ID).ToList()); 

Infine, si dovrebbe notare due cose in particolare: primo luogo, v'è un generico condizione sulla funzione where T : CommonBaseType. Suppongo che Categoria e Progetto abbiano un tipo di base comune o un'interfaccia che includa Nome. In caso contrario, dovresti eliminare la condizione e utilizzare Dynamic per ottenere il nome.

In secondo luogo, sto utilizzando Activator.Create per creare la classe per te. Questa è la parte difficile che rende difficile capire, se non si conosce quel trucco

Buona fortuna!

+0

Generics è una bella soluzione ... la tua chiamata dovrebbe aggiungere il tipo, no, vale a dire var currentCategories = DoYourThing (Categories.ToList(), Category.LoadForProject (project.ID) .ToList()) ; '? – Lazarus

+0

@Lasarus: No. Quando il tipo può essere dedotto dai parametri (come in questo caso), la dichiarazione del tipo sul metodo è ridondante. :) –

+0

@Brian Genisio il tuo diritto che è impossibile da trovare se non capisci la parte Activator.Create. Questo è semplice Genius. – msarchet

7

Fai priorità e la categoria implementare la stessa interfaccia o derivare da una classe con le proprietà comuni in esso (cioè .project, .Nome e .save). Quindi usa l'interfaccia o la classe base come tipo della tua funzione e potrai passare collezioni di entrambe le classi.

+0

Picchiami ... un po 'di codice ... – Lazarus

+0

@Lazarus era già a metà strada scrivendo il codice quando ho visto @Brian Genisio ha illustrato bene il punto! – w69rdy

0

Se Priority e Category sono entrambi derivati ​​dalla stessa classe base con un insieme comune di metodi/proprietà o implementare la stessa interfaccia, sì sì, è possibile. Sarà sufficiente sostituire i riferimenti specifici a Priority e Category con riferimenti a tale classe base o interfaccia (a seconda dei casi).

Ci sono alcune piccole differenze di codice (come il List<string>(Categories) nel primo blocco di codice) che dovrete pensare a come gestire, ma la maggior parte del codice andrà a posto una volta che la domanda di antenato/interfaccia sarà risolta.

+0

Non penso che questo sia un caso di ereditarietà in quanto la gerarchia di classi non avrebbe senso. Potresti creare una classe base arbitraria ma non seguirebbe lo spirito di essere un antenato comune alle classi figlie. Un'interfaccia ha molto più senso qui per fornire un'API comune a classi disparate. – Lazarus

0

Trovo che lo dynamic sia molto utile per insiemi di tipi che espongono la stessa proprietà, ma non implementano la stessa interfaccia.

Iterare l'elenco con foreach(dynamic d in myList)... d.Name..., racchiuderlo in un metodo e passare diverse istanze IList<object> (categorie o priorità).

Richiede C# 4.0.

+1

Mi sembra di imbrogliarmi ;-) –

+0

uguale a Jouke. questo è solo pigro qui. dinamico non è inteso che non è necessario definire le interfacce. – cRichter

+0

Grazie mille per aver risposto. Sono nuovo di C#. La prego di darmi il pezzo di codice usando dinamico. Grazie – learning

0

è necessario effettuare priorità e la categoria derivano dalla stessa classe di base ... e poi si potrebbe fare qualcosa sulla falsariga di:

public void ProcessLists<ItemType>(Func<int, IEnumerable<ItemType>> Loader) whereItemType : CommonBase, new() { 
List<string> items = new List<string>(); 
IList<ItemType> currentItems = Loader(project.ID).ToList(); 
if (currentItems != null) { 
    foreach (var existingItem in currentItems) { 
     if (items.Contains(existingItem.Name)) 
      items.Remove(existingItem.Name); 
     else 
      existingItem.Delete(Services.UserServices.User); 
    } 
    foreach (string item in items) { 
     ItemType item = new ItemType(); 
     item.Project = project 
     item.Name = item.ToString(); 
     item.Save(); 
    } 
} 

}

Naturalmente, alcuni tipi (ad esempio come project.ID) sono semplicemente azzeccati e dovrebbero essere sostituiti con le linee appropriate.

È possibile chiamare la funzione per la priorità in questo modo:

ProcessLists<Priority>(id => Priority.LoadForProject(project.ID)); 
+0

Con questa soluzione, si rimane bloccati quando è necessario chiamare il nuovo ItemType, in quanto deve essere diverso in base al tipo effettivo. I generici sono la via per andare qui. –

+0

@Brian: Hmmm, temo di non vedere il tuo punto. Potresti spiegarlo ulteriormente? – Christian

+0

@Christian: Nelle sue due diverse implementazioni, chiama nuove Category (args) e new Project (args). Non puoi semplicemente chiamare il nuovo ItemType(). Non otterrai il tipo che ti aspetti. È necessario rendere il metodo generico e chiamare Activator.Create (typeof (T), args) per creare la derivazione corretta di ItemType. –

1

Va bene, per quanto ho capito, si desidera aggiungere le categorie/priorità della "nuova" lista, che non sono existant nel repository.

farlo.

public void SaveNewItems<T>(IList<string> newList, IList<T> currentList, string project) 
    where T: new(), IStoreableItem 
{ 
    //find only new items 
    var toAdd = from itemName in newList 
       where !currentList.Contains(i => i.Name = itemName) 
       select new T { 
        Name = itemName, 
        Project = project 
       }; 


    //find items to delete 
    var toDelete = from item in currentList 
        where !newList.Contains(item.Name) 
        select item; 

    toAdd.ToList().ForEach(item => item.Save()); 
    toDelete.ToList().ForEach(item => item.Delete()); 
} 

Categoria e Prio devono derivare da IStoreableItem che contiene il nome, il progetto e salvare/metodo di cancellazione.

Problemi correlati