2015-03-17 2 views
3

Я хочу создать Shim для универсального метода. Но в этом случае у меня есть немного проблем с Generic.ШИМ и общие методы

Вот мой пример:

class BaseRepository <T> where T: Entity 
{ 
    public T[] FindAll() 
    { 
     return Method<T>.FindAll() 
    } 
} 

class ClassA : base<A> 
{ 
} 

class A : Entity 
{ 
} 

class ClassB : base<B> 
{ 
} 

class B : Entity 
{ 
} 

теперь я хочу, чтобы создать ShimMethod для ClassA и ClassB

ShimBaseRepository<A>.AllInstances.FindAll = (repo) => MethodA(); 
ShimBaseRepository<B>.AllInstances.FindAll = (repo) => MethodB(); 

public A MethodA() 
{ 
    //Make the Same as MethodB 
} 

public B MethodB() 
{ 
    //Make the Same as MethodA 
} 

Но что, если у меня есть мор чем 20 классов "Base"? Я не хочу создавать делегат/метод для каждого базового класса. Я пытался что-то вроде этого:

List<Type> allEntityClasses = (from x in Assembly.GetAssembly(typeof(Entity)).GetTypes() 
            where !x.IsAbstract && !x.IsInterface 
            select x).ToList(); 

foreach(Type type in allEntityClasses= 
{ 
    ShimBaseRepository<type????>.AllInstances.FindAll = (repo) => Method(); 
} 

public Entity????? Method() 
{ 
} 

В моей UnitTest я буду использовать следующие методы:

ClassA.FindAll() 
ClassB.FindAll() 

, а не:

Base.FindAll() 

Edit: Я использую Microsoft Фальшивок, поэтому я ничего не может изменить в ShimClass. Вот сгенерированный исходный код от Shim.

public class ShimBaseRepository<T> : ShimBase<BaseRepository<T>> where T : Entity 
    { 
    public static class AllInstances 
    { 
     public static FakesDelegates.Func<BaseRepository<T>, T[]> FindAll { [ShimMethod("FindAll", 20)] set; } 
    } 
    } 

Мое намерение состоит в том, что я не хочу, чтобы создать делегат для каждого объекта, я просто хочу, чтобы перебрать все мои EntityClasses и создать делегат динамически. Но у меня нет идеи, как я добавляю свой объект Type в

ShimBase<T> 
+0

В чем проблема, которую вы пытаетесь решить? Вы показываете нам решение, которое не работает, но не проблема, которую вы на самом деле имеете. Кроме того, убедитесь, что у вас есть пример кода, который можно запустить. – Luaan

ответ

2

Хорошо, давайте обсудим это немного. Прежде всего, вот прямолинейно решение с виртуальным способом:

public class Base<T> where T : Entity 
{ 
    public virtual T[] FindAll() 
    { 
     return null; 
    } 
} 

Тогда просто переопределить FindAll в конкретных классах Или, если вы можете, сделать Base абстрактные и InnerFindAll абстрактные тоже.

Но если вам нужно указать делегата во время выполнения (поскольку я вижу, что у вас есть определенный Помощник для него, но я не могу получить, почему вы вызываете помощника в базе, а затем у вас есть неопределенный вопрос AllInstances with Func), этот подход не поможет. Вам нужно будет реализовать Strategy pattern с некоторой стратегией по умолчанию, назначенной в Base. Тогда вы будете иметь 3 способа «разрешения» стратегий в конкретных классах:

  1. прописывай стратегию в конструктор конкретного класса
  2. Вводят стратегия конкретного конструктора класса с помощью DI контейнера
  3. Реализовать своего рода Mapper который вернет вам соответствующую стратегию для EntityType (T)

Кроме того, я думаю, что у вас есть проблемы с дизайном. Я не вижу причин, по которым вам нужно реализовать FindAll в качестве lambda, введенного в статическое свойство типа Func<T> (да, я думаю, вы можете заменить AllInstances.FindAll всего лишь static FindAll). Так что, если бы я был вами, я бы использовал абстрактный метод.

EDIT

Теперь я получил вашу проблему и может дать вам только довольно уродливое решение с помощью отражения ... Я hoghly не рекомендую вам использовать это, так как это действительно трупное

public class Program 
{ 
    static void Main(string[] args) 
    { 
     List<Type> allEntityClasses = (from x in Assembly.GetAssembly(typeof(Entity)) 
              .GetTypes().Where(t=>typeof(Entity).IsAssignableFrom(t)) 
             where !x.IsAbstract && !x.IsInterface 
             select x).ToList(); 
     foreach (var type in allEntityClasses) 
     { 
      var genericType = typeof(BaseGeneric<>).MakeGenericType(type); 
      var helper = new DelegateHelper(); 
      var myLambda = helper.GetLambdaForType(type); 
      var allInst = genericType.GetProperty("AllInstances").GetValue(null); 
      if (allInst == null) 
      { 
       allInst = Activator.CreateInstance(genericType.GetProperty("AllInstances").PropertyType); 
      } 
      allInst.GetType().GetProperty("FindAll").SetValue(allInst,myLambda); 
     } 
    } 


} 

public static class BaseGeneric<T> 
{ 
    public static AllInstances<T> AllInstances { get; set; } 
} 

public class AllInstances<T> 
{ 
    public Func<T[]> FindAll { get; set; } 
} 

public class DelegateHelper 
{ 
    public Delegate GetLambdaForType(Type type) 
    { 
     var funcType = typeof(Func<>).MakeGenericType(type.MakeArrayType()); 
     var methodInfo = typeof(DelegateHelper).GetMethods().FirstOrDefault(t => t.Name == "FunctionMethod") 
               .MakeGenericMethod(type); 
     var @delegate = methodInfo.CreateDelegate(funcType, this); 
     return @delegate; 
    } 

    public T[] FunctionMethod<T>() 
    { 
     return new T[10]; 
    } 
} 

public class Entity 
{ 
} 

public class EntityFirst 
{ 

} 

public class EntitySecond 
{ 

} 
+0

Я использую Microsoft.Fakes для тестирования и не писал эти классы. Я просто хочу его использовать. Я добавил сгенерированный исходный код из подделок microsoft. – wydy

+0

Это также мой первый раз с Microsoft Fake, и это причина моих проблем. У меня есть BaseRepository где T: Entity , которые получают некоторые данные из интерфейса. Теперь я пишу некоторые Unittests, и я не хочу/не могу пройти через этот интерфейс. Поэтому я «подделываю» этот интерфейс и переопределяю методы BaseRepository . С ShimBase .AllInstances.FindAll = (repo) => Method(); Я использую метод testmethod вместо интерфейса. Теперь мне нужно создать делегат для каждого EntityClass, или программа попытается пройти через интерфейс. Но есть около 100 EntityClasses ... – wydy

+0

@wydy я обновил свой ответ, пожалуйста, дайте мне знать, если это поможет. Он много использует отражение, и поэтому я нахожу его действительно уродливым и не ремонтируемым. –

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