2013-08-09 6 views
9

Я пытаюсь выяснить, как упростить следующиеВызов функции в зависимости от типа параметра

скажем, у меня есть 2 объектные классы

public class A 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public string City { get; set; } 
} 

И

public class B 
{ 
    public int Id { get; set; } 
    public string Nom { get; set; } 
    public string Ville { get; set; } 
} 

классы, которые похоже, но не то же самое.

каждый класс имеет репозиторий классов, которые он использует для CRUD операций, например ...

public class RepA 
{ 
    public static List<A> GetAll() 
    { 
     List<A> list = new List<A>(); 

     A a1 = new A() {Id=1, Name="First A", City="Boston"}; 
     A a2 = new A() {Id=2, Name="First B", City="Chicago"}; 
     A a3 = new A() {Id=3, Name="First C", City="San Francisco"}; 

     list.Add(a1); 
     list.Add(a2); 
     list.Add(a3); 
     return list; 
    } 

    public static void SaveAll(List<A> list) 
    { 
     foreach (A a in list) 
     { 
       Console.WriteLine("Saved Id = {0} Name = {1} City={2}", 
        a.Id, a.Name, a.City); 
     } 
    } 

} 

И

public class RepB 
    { 
     public static List<B> GetAll() 
     { 
      List<B> list = new List<B>(); 

      B b1 = new B() {Id=1, Nom="Second A", Ville="Montreal"}; 
      B b2 = new B() {Id=2, Nom="Second B", Ville="Paris"}; 
      B b3 = new B() {Id=3, Nom="Second C", Ville="New Orleans"}; 

      list.Add(b1); 
      list.Add(b2); 
      list.Add(b3); 
      return list; 
     } 

    public static void SaveAll(List<B> list) 
    { 
     foreach (B b in list) 
     { 
      Console.WriteLine("Saved Id = {0} Name = {1} City={2}", b.Id, 
        b.Nom, b.Ville); 
     } 
    } 

} 

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

void Main() 
{ 
    ChosenType chosentype = RandomChosenType(); //A or B 
    switch (chosentype) 
    { 
     case ChosenType.A: 
      var listA = RepA.GetAll(); 
      RepA.SaveAll(listA); 
      break; 
     case ChosenType.B: 
      var listB = RepB.GetAll(); 
      RepB.SaveAll(listB); 
      break; 
      default: 
      break; 
    } 
} 
+4

Похоже, вы пытаетесь сохранить отдельную таблицу на локализацию - не очень хорошая идея. Лучше быть последовательным на бэкэнд и только локализовать для целей показа. – James

+1

Локализация структур данных кажется плохой идеей. Не уверен, что вы выиграете от этого. Это просто сделает ваш код сложным для написания и поддержки. Действительно ли программисты могут читать каждый язык? –

+0

Получите все классы, помещенные в список, и вызовите общие методы в соответствии с выбранным классом, перейдите в один из них в Switch и сделайте LIST как Generic Keyword и добавьте список + «ClassName» ... – Aravind

ответ

3

Сделать base class или используйте interface:

public interface IBase<T> 
{ 
    List<T> GetAll(); 
    void SaveAll(List<T> items); 
} 

public class RepA : IBase<RepA> 
{ 
    public List<RepA> GetAll() { return new List<RepA>(); } 
    public void SaveAll(List<RepA> repA) { } 
} 

public class RepB : IBase<RepB> 
{ 
    public List<RepB> GetAll() { return new List<RepB>(); } 
    public void SaveAll(List<RepB> repB) { } 
} 

void Main() 
{ 
    IBase chosenType = RandomChosenType(); 
    var list = chosenType.GetAll(); 
} 
+0

Это будет работать, только если 'IBase' был общим,' GetAll'/'SaveAll' использует строго типизированные списки. – James

+0

GetAll должен вернуть Список BlackBear

+0

@BlackBear - скорректирован. –

1

Вы можете сделать свои хранилища реализовать интерфейс, скажу IGetAllSaveAll. Затем вы можете хранить свои репозитории в списке и передавать их в этот интерфейс. Таким образом, вы будете в состоянии вызвать GetAll функцию на всех из них: (на самом деле первый интерфейс не является обязательным, вы можете сразу написать его как IEnumerable<object> GetAll() ...)

interface IGetAllSaveAll<T> 
{ 
    IEnumerable<T> GetAll(); 
    void SaveAll(IEnumerable<T> obj); 
} 

вам нужно иметь базовый интерфейс:

interface IGetAllSaveAll : IGetAllSaveAll<object> 

и использовать его:

public class RepA: IGetAllSaveAll 
public class RepB: IGetAllSaveAll 
.... 

Тогда вы можете сохранить dictionnary всех этих хранилищ где:

Dictionnary<Type, IGetAllSaveAll> myDic; 

Конечно, вы все равно должны будете добавлять репозитории вашего dictionnary:

myDic.Add(typeof(A), new RepA()); 

А потом называют это:

Type t = RandomChosenType(); 
myDic[t].GetAll(); 
+0

Вероятно безопасно предположить 'IGetAllSaveAll >' в этом случае. – James

+0

@James на самом деле я сделал это 'IEnumerable', но по методам, кажется лучше – ppetrov

+0

oops, это была моя ошибка, вот что я на самом деле имел в виду haha! – James

0

Попробуйте этот подход, основанный на отражении и некоторые предположения о структурах ваших классов:

static void Main(string[] args) 
{ 
    var types = Assembly.GetExecutingAssembly().Modules 
     .SelectMany(m => m.GetTypes()) 
     .Where(t => 
      t.GetMethod("GetAll") != null && 
      t.GetMethod("SaveAll") != null && 
      t.GetMethod("GetAll").ReturnType.IsGenericType) 
     .Select(t => 
      new 
      { 
       RepositoryType = t, 
       ReturnTypeArgument = 
        t.GetMethod("GetAll").ReturnType.GenericTypeArguments[0] 
      } 
      ) 
     .ToList(); 

    (new List<dynamic> { new A(), new B() }).ToList().ForEach(chosenType => 
    { 
     var association = types 
      .FirstOrDefault(t => 
       t.ReturnTypeArgument == chosenType.GetType()); 
     if (association == null) 
      return; 
     var repType = association.RepositoryType; 
     dynamic list = repType.GetMethod("GetAll") 
      .Invoke(chosenType, new object[] { }); 
     repType.GetMethod("SaveAll") 
      .Invoke(chosenType, new object[] { list }); 
    }); 
} 
0

Введенный вами код использует статические методы. Чтобы реализовать интерфейс, вам понадобятся методы экземпляра. Если вы не хотите использовать отражение (следует избегать, на мой взгляд), эти методы должны быть незнакомы с типом.Что-то вроде этого:

public interface IRepository { 
    IEnumerable<object> GetAll(); 
} 

И Репа:

IEnumerable<object> IRepository.GetAll() { 
    return RepA.GetAll(); 
} 

Вместо хранения типов, каждый из выбранных параметров меню может содержать только экземпляр соответствующего класса хранилища в поле типа IRepository. После вызова GetAll в одном из экземпляров вы можете позже привести результат к определенному типу (например, List<A>), если необходимо.

+0

Я думаю, что мы можем быть на чем-то здесь :) позвольте мне попробовать, что – SerenityNow

3

Вы должны использовать один общий репозиторий. Операции должны обрабатываться проиндексированными делегатами. Репозиторий может выглядеть так:

public class GenericRepositoryExample 
{ 

    public void Save<T>(IList<T> persons, SaveDelegate<T> save) 
    { 
     foreach (T person in persons) 
     { 
      Console.WriteLine(save(person)); 
     } 
    } 
} 

Обратите внимание, что делегат сохранения передается методу сохранения. SaveDelegate в вашем примере может быть объявлен как:

public delegate string SaveDelegate<T>(T input); 

Для простоты, я создал вспомогательный класс, содержащий делегированные функции. В реальной жизни, по возможности, следует избегать классов-помощников.

public static class HelperClass 
{ 
    public static string FrenchSave(B frenchInput) 
    { 

     string result = string.Format("ID = {0}; Name = {1}; City = {2}", frenchInput.Id, frenchInput.Nom, frenchInput.ville); 
     return result; 
    } 

    public static string EnglishSave(A englishInput) 
    { 
     string result = string.Format("ID = {0}; Name = {1}; City = {2}", englishInput.Id, englishInput.name, englishInput.city); 
     return result; 
    } 

} 

Для иллюстрации использования этой установки, я создал следующий тест блока:

[Test] 
    public void TestGenericRepository() 
    { 
     IList<A> aList = new List<A>(); 

     aList.Add(new A() { Id = 1, name = "George", city = "Chicago"}); 
     aList.Add(new A() { Id = 2, name = "Bill", city = "Toledo" }); 


     List<B> bList = new List<B>(); 

     bList.Add(new B() {Id= 1, Nom = "Nathalie", ville = "Paris"}); 
     bList.Add(new B() {Id = 2, Nom = "Michelle", ville = "Lyon"}); 


     GenericRepositoryExample repository = new GenericRepositoryExample(); 

     repository.Save<A>(aList,HelperClass.EnglishSave); 

     repository.Save<B>(bList,HelperClass.FrenchSave); 

    } 
+0

хорошо, но мне все еще нужно знать A и тип .... так что если у меня есть 100 репозиториев, моей реализации по-прежнему потребуется 100 разных вызовов или я что-то упустил Что мне нужно - это настройка, которая позволяет мне записать Сохранить (список) и базу на в списке, он будет знать, с чего сохранить, чтобы позвонить и сделать это без переключения – SerenityNow

+0

@RedSoxFred. Я не совсем понимаю, что вы имеете ввиду. Вы хотите сохранить список, содержащий как A, так и B? – Morten

+0

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

0

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

Сопоставьте каждое значение перечисления с типом репозитория с использованием атрибута. Каждый репозиторий наследуется от общего класса, который реализует базовый интерфейс, который не сильно типизирован. Методы репо меняются от статических элементов к экземплярам. Базовому классу репо нужно делать литье, чтобы отличить object с соответствующим типом и обратно, но фактические реализации репозитория строго типизированы.

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

public enum ChosenType { 
    [Repo(typeof(RepA))] A = 0, 
    [Repo(typeof(RepB))] B = 1 
} 

public class RepoAttribute : Attribute { 
    public RepoAttribute(Type repoType) { RepoType = repoType; } 
    public Type RepoType { get; set; } 
} 

class Program 
{ 
    static void Main() 
    { 
     ChosenType chosentype = RandomChosenType(); //A or B 

     // Make an instance of the appropriate repo based on the mapping 
     // to the enum value. 
     // This is a moderately expensive call, and there's room for improvement 
     // by using expression trees and caching lambda expressions. 
     var repo = (IRepo)Activator.CreateInstance(
      ((RepoAttribute)typeof(ChosenType).GetMember(chosentype.ToString()) 
       .Single().GetCustomAttributes(typeof(RepoAttribute), false).Single() 
      ).RepoType); 

     var list = repo.GetAll(); 
     repo.SaveAll(list); 

     Console.Read(); 
    } 

    static Random _rand = new Random(); 
    static ChosenType RandomChosenType() 
    { 
     return (ChosenType)_rand.Next(0, 2); 
    } 
} 

public class A { /* No change */ } 
public class B { /* No change */ } 

public interface IRepo { 
    List<object> GetAll(); 
    void SaveAll(List<object> list); 
} 

public abstract class Repo<T> : IRepo { 
    List<object> IRepo.GetAll() { 
     return GetAll().Cast<object>().ToList(); 
    } 

    void IRepo.SaveAll(List<object> list) { 
     SaveAll(list.Cast<T>().ToList()); 
    } 

    public abstract List<T> GetAll(); 
    public abstract void SaveAll(List<T> list); 
} 

public class RepA : Repo<A> { 
    public override List<A> GetAll() { /* No change except the signature */ } 
    public override void SaveAll(List<A> list) { /* No change except the signature */ } 
} 
public class RepB : Repo<B> { 
    public override List<B> GetAll() { /* No change except the signature */ } 
    public override void SaveAll(List<B> list) { /* No change except the signature */ } 
} 
Смежные вопросы