2010-07-08 7 views
7

У меня есть следующий код, и я хотел бы написать его так, чтобы у меня были минимальные строки кода, и работа выполняется одинаково. Как я могу это сделать?Как избежать дублирования кода

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

ответ

9

Что-то вроде этого следует сделать это:

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

Тогда вы можете назвать это так:

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

Наконец, следует отметить две вещи, в частности: Во-первых, общий состояние на функции where T : CommonBaseType. Я предполагаю, что категория и проект имеют общий базовый тип или интерфейс, который включает имя. Если нет, вы должны избавиться от этого условия и использовать Dynamic для получения имени.

Во-вторых, я использую Activator.Create, чтобы создать класс для вас. Это сложная часть, которая затрудняет определение, если вы не знаете, что трюк

Удачи!

+0

Generics - отличное решение ... вашему звонку потребуется добавить тип, не так ли, то есть 'var currentCategories = DoYourThing (Категории.ToList(), Category.LoadForProject (project.ID) .ToList()) ; '? – Lazarus

+0

@Lasarus: Нет. Когда тип может быть выведен параметрами (как в этом случае), объявление типа в методе является избыточным. :) –

+0

@Brian Genisio ваше право, которое невозможно получить, если вы не понимаете Activator.Create part. Это простой гений. – msarchet

7

Make Приоритет и категории реализуют одинаковый интерфейс или вывести из класса с общими свойствами в нем (т.е. .project, .name и .save). Затем используйте этот интерфейс или базовый класс в качестве типа вашей функции, и вы сможете передавать коллекции обоих классов.

+0

Beat me to it ... как насчет какого-то кода ... – Lazarus

+0

@ Lazarus был на полпути, хотя писал код для него, когда я увидел @Brian Genisio, хорошо проиллюстрировал точку! – w69rdy

0

Еслии Category либо оба являются производными от того же базового класса с общим набором методов/свойств, либо реализуют один и тот же интерфейс, то да, вы можете. Вам просто нужно будет заменить конкретные ссылки на Priority и Category со ссылками на этот базовый класс или интерфейс (если применимо).

Есть несколько незначительных различий кода (например, List<string>(Categories) в первом блоке кода), которые вам придется подумать о том, как обращаться с ними, но большая часть кода просто встанет на место после разрешения вопроса о предке/интерфейсе.

+0

Я не думаю, что это случай для наследования, поскольку иерархия классов не имеет смысла. Вы можете создать произвольный базовый класс, но он не будет следовать духу быть обычным предком для дочерних классов. Интерфейс здесь имеет гораздо больше смысла, чтобы обеспечить общий «API» для разграничения классов. – Lazarus

0

Я нахожу dynamic очень полезным для наборов типов, которые выставляют одно и то же свойство, но не реализуют один и тот же интерфейс.

Итерации по списку с помощью foreach(dynamic d in myList)... d.Name..., оберните его в метод и передайте разные экземпляры IList<object> (категории или приоритеты).

Требуется C# 4.0.

+1

Это просто чувствует, как обман для меня ;-) –

+0

такой же, как Jouke. Здесь просто лениво. динамический не предназначен для определения интерфейсов. – cRichter

+0

Спасибо, много за ответ. Я новичок в C#. Не могли бы вы дать мне часть кода, используя динамический. Спасибо – learning

0

Вам нужно сделать Priority и категории проистекают из того же базового класса ... и тогда вы могли бы сделать что-то вдоль линий:

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

}

Конечно, некоторые виды (такие как проект.ID) просто догадываются и должны быть заменены соответствующими строками.

Вы можете вызвать функцию приоритета таким образом:

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

С помощью этого решения вы застреваете, когда вам нужно вызвать новый ItemType, так как он должен быть различным в зависимости от фактического типа. Дженерики - это путь сюда. –

+0

@Brian: Хммм, боюсь, я не вижу твоей точки. Не могли бы вы объяснить это дальше? – Christian

+0

@Christian: В двух вариантах он называет новую категорию (args) и новый проект (args). Вы не можете просто вызвать новый ItemType(). Вы не получите ожидаемого типа. Вам необходимо сделать метод общим и вызвать Activator.Create (typeof (T), args), чтобы создать правильный вывод ItemType. –

1

Хорошо, , насколько я понял, вы хотите добавить категории/приоритеты «нового» списка, которые не просуществовавшего в репозитории.

сделать это.

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

Категория и Прио должны извлечь из IStoreableItem, который содержит имя, проект и сохранения/удаления метода.

Смежные вопросы