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

}

私の現実の例では、2 ではなく 100 のリポジトリがあるため、これに頼らずにリポジトリへの匿名呼び出しを行うにはどうすればよいでしょうか。

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

6 に答える 6

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();
}
于 2013-08-09T13:01:26.020 に答える
3

単一の汎用リポジトリを使用する必要があります。操作は、注入されたデリゲートによって処理される必要があります。リポジトリは次のようになります。

public class GenericRepositoryExample
{

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

保存デリゲートが Save メソッドに渡されることに注意してください。あなたの例の SaveDelegate は次のように宣言できます。

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

簡単にするために、委任された関数を含む HelperClass を作成しました。実際のヘルパー クラスは、可能であれば一般的に避けるべきです。

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

    }
于 2013-08-09T14:48:01.723 に答える
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<Type, IGetAllSaveAll> myDic;

もちろん、リポジトリを辞書に追加する必要があります。

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

そして、それを呼び出すには:

Type t = RandomChosenType();
myDic[t].GetAll();
于 2013-08-09T13:03:59.913 に答える
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 */ }
}
于 2013-08-09T15:21:42.843 に答える
0

投稿したコードは静的メソッドを使用しています。インターフェイスを実装するには、インスタンス メソッドが必要です。リフレクションを使用したい場合を除き (私の意見では避けるべきです)、これらのメソッドは型を認識しない必要があります。このようなもの:

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

そして RepA では:

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

タイプを格納する代わりに、各メニュー選択に適切なリポジトリ クラスのインスタンスを type のフィールドに含めることができますIRepository。インスタンスの 1 つを呼び出しGetAllた後、必要に応じて後で結果を特定の型 ( などList<A>) にキャストできます。

于 2013-08-09T13:35:30.667 に答える