根据参数类型调用函数
本文关键字:调用 函数 类型 参数 | 更新日期: 2023-09-27 18:26:56
我正在尝试弄清楚如何简化以下内容
假设我有 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;
}
}
创建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();
}
您应该使用单个通用存储库。操作应由注入的委托处理。存储库可能如下所示:
public class GenericRepositoryExample
{
public void Save<T>(IList<T> persons, SaveDelegate<T> save)
{
foreach (T person in persons)
{
Console.WriteLine(save(person));
}
}
}
请注意,保存委托将传递给 Save 方法。示例中的保存委托可以声明为:
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);
}
你可以让你的仓库实现一个接口,比如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();
您发布的代码使用静态方法。为了实现接口,您将需要实例方法。除非你想使用反射(在我看来应该避免(,否则这些方法需要忽略类型。像这样:
public interface IRepository {
IEnumerable<object> GetAll();
}
在 RepA 中:
IEnumerable<object> IRepository.GetAll() {
return RepA.GetAll();
}
每个菜单选择都可以在类型为 IRepository
的字段中仅包含相应存储库类的实例,而不是存储类型。在其中一个实例上调用 GetAll
后,如有必要,稍后可以将结果强制转换为特定类型(如 List<A>
(。
根据反思和对类结构的一些假设,尝试这种方法:
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 });
});
}
鉴于您的确切方案,您有一个表示每种可能数据类型的枚举,这里有一些可能可行的方法。
使用属性将每个枚举值映射到存储库类型。每个存储库都继承自一个泛型类,该类实现了一个非强类型的基本接口。存储库方法从静态成员更改为实例成员。基存储库类必须执行强制转换才能将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 */ }
}