Shim 和通用方法
Shim and Generic Methods
我想为通用方法创建垫片。但在那种情况下,我对 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 个 "Base" 类 怎么办?我不想为每个 baseClass 创建一个 Delegate/method。我试过这样的事情:
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()
{
}
在我的单元测试中,我将使用以下方法:
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; }
}
}
我的意图是,我不想为每个实体创建委托,我只想遍历所有 EntityClass 并动态创建委托。但是我不知道如何在
中添加我的类型对象
ShimBase<T>
好的,让我们稍微讨论一下。
首先,这是一个使用虚方法的直接解决方案:
public class Base<T> where T : Entity
{
public virtual T[] FindAll()
{
return null;
}
}
然后在具体的 classes 中覆盖 FindAll
或者,如果可以的话,将 Base
和 InnerFindAll
也抽象化。
但是,如果您需要在运行时指定委托(我可以看到您有一个特定的 Helper,但我无法理解,为什么您在 Base 中调用 helper 然后您有一些未定义的问题 AllInstances使用 Func) 这种方法无济于事。您需要使用在 Base 中分配的一些默认策略来实施 Strategy pattern。然后,您将有 3 种方法 "resolve" 具体 class 策略:
- 在具体的构造函数中硬编码策略class
- 通过 DI 容器将策略注入具体 class 构造函数
- 实施某种 Mapper,它将 return 您为 EntityType (
T
) 应用策略
另外,我觉得你在设计上有一些问题。我看不出有任何理由需要将 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
{
}
我想为通用方法创建垫片。但在那种情况下,我对 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 个 "Base" 类 怎么办?我不想为每个 baseClass 创建一个 Delegate/method。我试过这样的事情:
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()
{
}
在我的单元测试中,我将使用以下方法:
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; }
}
}
我的意图是,我不想为每个实体创建委托,我只想遍历所有 EntityClass 并动态创建委托。但是我不知道如何在
中添加我的类型对象ShimBase<T>
好的,让我们稍微讨论一下。 首先,这是一个使用虚方法的直接解决方案:
public class Base<T> where T : Entity
{
public virtual T[] FindAll()
{
return null;
}
}
然后在具体的 classes 中覆盖 FindAll
或者,如果可以的话,将 Base
和 InnerFindAll
也抽象化。
但是,如果您需要在运行时指定委托(我可以看到您有一个特定的 Helper,但我无法理解,为什么您在 Base 中调用 helper 然后您有一些未定义的问题 AllInstances使用 Func) 这种方法无济于事。您需要使用在 Base 中分配的一些默认策略来实施 Strategy pattern。然后,您将有 3 种方法 "resolve" 具体 class 策略:
- 在具体的构造函数中硬编码策略class
- 通过 DI 容器将策略注入具体 class 构造函数
- 实施某种 Mapper,它将 return 您为 EntityType (
T
) 应用策略
另外,我觉得你在设计上有一些问题。我看不出有任何理由需要将 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
{
}