垫片和通用方法

时间:2015-03-17 07:58:21

标签: c# generics shim

我想为通用方法创建一个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
{
}

现在我想为ClassA和ClassB创建一个ShimMethod

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个“基础”课程怎么办?我不想为每个baseClass创建一个Delegate /方法。我试过这样的事情:

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

编辑: 我使用Microsoft Fakes,所以我无法更改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>

1 个答案:

答案 0 :(得分:2)

好的,让我们稍微讨论一下。 首先,这是一个使用虚拟方法的直接解决方案:

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

然后在具体类中覆盖FindAll 或者,如果可以的话,也可以使Base抽象,InnerFindAll抽象。

但是,如果你需要在运行时指定委托(因为我可以看到你有一个特定的帮助器,但我无法得到,为什么你在Base中调用帮助器然后你有一些未定义的问题AllInstances与a Func)这种方法无济于事。您需要在Base中分配一些默认策略来实现Strategy pattern。然后,您将有3种方法在具体类中“解决”策略:

  1. 在具体类的构造函数中对策略进行硬编码
  2. 通过DI容器向具体类构造函数注入策略
  3. 实现某种Mapper,它会返回适当的EntityType策略(T
  4. 另外,我认为你在设计方面遇到了一些麻烦。我没有看到任何理由需要将FindAll实现为注入类型为Func<T>的静态属性的lambda(是的,我认为你可以用AllInstances.FindAll替换static FindAll)。所以,如果我是你,我会使用抽象方法..

    修改

    现在我遇到了你的问题并且只能通过反思给你一个相当丑陋的解决方案......我不建议你使用它,因为它非常严格

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