2010-07-08 14 views
7

Tengo el siguiente código y me gustaría escribirlo de manera que tenga un mínimo de líneas de código y el trabajo se hace de la misma manera. ¿Cómo puedo hacer eso?¿Cómo puedo evitar la duplicación de código?

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

Respuesta

9

Algo así debe hacerlo:

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

Entonces se le puede llamar así:

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

Por último, debe tener en cuenta dos cosas en particular: primer lugar, existe un genérico condición en la función where T : CommonBaseType. Supongo que Categoría y Proyecto tienen un tipo de base común o interfaz que incluye Nombre. De lo contrario, debe deshacerse de la condición y usar Dynamic para obtener el nombre.

En segundo lugar, estoy usando Activator.Create para crear la clase para ti. Esta es la parte difícil que hace que sea difícil de entender, si no conoces ese truco

¡Buena suerte!

+0

genéricos es una buena solución ... su llamada necesitaría agregar el tipo ¿no es así, es decir 'var currentCategories = DoYourThing (Categories.ToList(), Category.LoadForProject (project.ID) .ToList()) ; '? – Lazarus

+0

@Lasarus: No. Cuando los parámetros pueden inferir el tipo (como en este caso), la declaración de tipo en el método es redundante. :) –

+0

@Brian Genisio su derecho que es imposiblemente difícil de conseguir si no entiende el Activador.Crea parte. Esto es simple Genius. – msarchet

7

Prioridad Marca y Categoría implementar la misma interfaz o se derivan de una clase con las propiedades comunes en el mismo (es decir, .project, .Name y .save). Luego use esa interfaz o clase base como el tipo de su función y podrá pasarle colecciones de ambas clases.

+0

Muéstremelo ... ¿qué tal un código ... – Lazarus

+0

@Lazarus estaba a medio camino escribiendo código para él cuando vi @Brian Genisio ha ilustrado el punto muy bien! – w69rdy

0

Si Priority y Category derivan de la misma clase base con un conjunto común de métodos/propiedades o implementan la misma interfaz, entonces sí, puede. Tan solo deberá reemplazar las referencias específicas a Priority y Category con referencias a esa clase base o interfaz (según corresponda).

Hay algunas diferencias de código menores (como el List<string>(Categories) en el primer bloque de código) que tendrá que pensar en cómo manejar, pero la mayor parte del código se colocará una vez que se resuelva la pregunta de antepasado/interfaz.

+0

No creo que esto sea un caso de herencia, ya que la jerarquía de clases no tendría sentido. Podrías crear una clase base arbitraria pero no seguiría el espíritu de ser un ancestro común para las clases de niños. Una interfaz tiene mucho más sentido aquí para proporcionar una 'API' común para clases dispares. – Lazarus

0

Encuentro dynamic muy útil para conjuntos de tipos que exponen la misma propiedad, pero no implementan la misma interfaz.

Iterate a través de la lista con foreach(dynamic d in myList)... d.Name..., envuélvela en un método y pasa diferentes instancias IList<object> (categorías o prioridades).

Requiere C# 4.0.

+1

Eso solo me parece hacer trampa ;-) –

+0

igual que Jouke. eso es perezoso aquí. dinámico no significa que no necesite definir interfaces. – cRichter

+0

Gracias, mucho por responder. Soy nuevo en C#. ¿Podrías darme el código usando la dinámica? Gracias – learning

0

Necesitas hacer prioridad y categoría se derivan de la misma clase base ... y entonces se podría hacer algo a lo largo de las líneas de:

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

}

Por supuesto, algunos tipos (tales como el project.ID) simplemente son adivinados y deberían ser reemplazados con las líneas apropiadas.

Puede llamar a la función de prioridad de esta manera:

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

Con esta solución, se queda atascado cuando necesita llamar al nuevo ItemType, ya que debe ser diferente según el tipo real. Los genéricos son el camino a seguir aquí. –

+0

@Brian: Hmmm, me temo que no entiendo tu punto. ¿Podrías explicarlo más? – Christian

+0

@Christian: en sus dos implementaciones diferentes, llama a Categoría nueva (args) y Proyecto nuevo (args). No puede simplemente llamar a nuevo ItemType(). No obtendrás el tipo que esperas. Necesita hacer que el método sea genérico, y llame a Activator.Create (typeof (T), args) para crear la derivación correcta de ItemType. –

1

bien, por lo que he entendido, que desea agregar las categorías/prioridades de la lista "nueva", que no están al loro en el repositorio.

hacer eso.

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

Categoría Prio y deben derivar de IStoreableItem que contiene el nombre, proyecto, y guardar/borrar método.

Cuestiones relacionadas