2010-07-08 15 views
7

Ich habe den folgenden Code und ich möchte es auf eine Weise schreiben, dass ich minimale Zeilen Code habe und die Arbeit auf die gleiche Weise erledigt wird. Wie kann ich das machen?Wie kann ich Code-Duplizierung vermeiden

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

Antwort

9

So etwas sollte es tun:

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

Dann können Sie es so nennen:

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

Schließlich sollten Sie vor allem zwei Dinge beachten: Erstens gibt es ein generisches Bedingung für die Funktion where T : CommonBaseType. Ich nehme an, dass Kategorie und Projekt einen gemeinsamen Basistyp oder eine Schnittstelle haben, die Name enthält. Wenn nicht, sollten Sie die Bedingung loswerden und Dynamic verwenden, um Name zu erhalten.

Zweitens verwende ich Activator.Create, um die Klasse für Sie zu erstellen. Dies ist der schwierige Teil, der es schwierig macht herauszufinden, ob Sie diesen Trick nicht kennen

Viel Glück!

+0

Generics ist eine nette Lösung ... Ihr Aufruf müsste den Typ hinzufügen würde es nicht, dh 'var currentCategories = DoYourThing (Categories.ToList(), Category.LoadForProject (project.ID) .ToList()) ; '? – Lazarus

+0

@Lasarus: Nein. Wenn der Typ von den Parametern abgeleitet werden kann (wie in diesem Fall), ist die Typdeklaration für die Methode redundant. :) –

+0

@Brian Genisio Ihr Recht, das ist unglaublich schwer zu kommen, wenn Sie den Activator.Create Teil nicht verstehen. Das ist ein einfaches Genie. – msarchet

7

Make Priorität und Kategorie die gleiche Schnittstelle implementieren oder leiten sich von einer Klasse mit den gemeinsamen Eigenschaften in ihm (das heißt .Projektdaten, .Name und .Save). Verwenden Sie dann diese Schnittstelle oder Basisklasse als den Typ Ihrer Funktion, und Sie können Sammlungen beider Klassen an sie übergeben.

+0

Beat mich dazu ... wie wäre es mit einem Code ... – Lazarus

+0

@Lazarus war auf halbem Weg Code für es zu schreiben, als ich sah @Brian Genisio hat den Punkt schön illustriert! – w69rdy

0

Wenn Priority und Category entweder beide aus der gleichen Basisklasse mit einem gemeinsamen Satz von Methoden/Eigenschaften abgeleitet sind, oder implementieren Sie die gleiche Schnittstelle dann ja, können Sie. Sie müssen nur die spezifischen Verweise auf Priority und Category durch Verweise auf diese Basisklasse oder Schnittstelle (wie zutreffend) ersetzen.

Es gibt ein paar kleinere Codedifferenzen (wie die List<string>(Categories) im ersten Codeblock), die Sie über die Handhabung nachdenken müssen, aber der meiste Code wird einfach in den Platz fallen, sobald die Vorfahr/Interface-Frage erledigt ist.

+0

Ich denke nicht, dass dies ein Fall für die Vererbung ist, da die Klassenhierarchie keinen Sinn ergibt. Sie könnten eine beliebige Basisklasse erstellen, aber sie würde nicht dem gemeinsamen Vorfahren der Kindklassen folgen. Eine Schnittstelle ist hier viel sinnvoller, um eine gemeinsame "API" für verschiedene Klassen bereitzustellen. – Lazarus

0

Ich finde dynamic sehr nützlich für Arten von Typen, die die gleiche Eigenschaft offen legen, aber nicht die gleiche Schnittstelle implementieren.

Iterieren Sie durch die Liste mit foreach(dynamic d in myList)... d.Name..., wickeln Sie es in eine Methode und übergeben Sie verschiedene IList<object> Instanzen (Kategorien oder Prioritäten).

Benötigt C# 4.0.

+1

Das fühlt sich für mich einfach so an ;-) –

+0

wie Jouke. das ist nur faul hier. Mit dynamic ist nicht beabsichtigt, dass Sie keine Schnittstellen definieren müssen. – cRichter

+0

Danke, viel zur Beantwortung. Ich bin neu in C#. Könnten Sie mir bitte den Code mit dynamic geben? Danke – learning

0

Sie müssen Priorität und Kategorie machen aus der gleichen Basisklasse ableiten ... und dann könnte man etwas entlang der Linien von tun:

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

}

Natürlich einige Arten (wie als project.ID) sind nur erraten und sollten durch die richtigen Zeilen ersetzt werden.

Sie die Funktion zur Priorität in dieser Art und Weise können auch anrufen:

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

Mit dieser Lösung bleiben Sie hängen, wenn Sie einen neuen ItemType aufrufen müssen, da dieser abhängig vom tatsächlichen Typ unterschiedlich sein muss. Generika sind der Weg dorthin. –

+0

@Brian: Hmmm, ich fürchte, ich verstehe deinen Standpunkt nicht. Kannst du es weiter erklären? – Christian

+0

@Christian: In seinen zwei verschiedenen Implementierungen ruft er neue Category (args) und neue Project (args) auf. Sie können nicht einfach einen neuen ItemType() aufrufen. Sie werden nicht den Typ erhalten, den Sie erwarten. Sie müssen die Methode generisch machen und Activator.Create (typeof (T), args) aufrufen, um die korrekte Ableitung von ItemType zu erstellen. –

1

Okay, soweit ich verstanden, möchten Sie die Kategorien/Prioritäten der „neuen“ Liste hinzuzufügen, die nicht existente werden im Repository.

machen Sie das.

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

Kategorie und Prio müssen von IStoreableItem ableiten, die den Namen, Projekt enthält, und speichern/Methode löschen.

Verwandte Themen