【问题标题】:Pass parameter to function via reflection通过反射将参数传递给函数
【发布时间】:2016-12-30 19:21:10
【问题描述】:

我有一个带有以下签名的扩展方法

public static void AddConfiguration<TEntity>(this ModelBuilder builder, EntityTypeConfiguration<TEntity> configuration)
    where TEntity : class
{
    ...
}

我想通过Reflection传参数,试过这个方法:

var ctors = type.GetConstructors(BindingFlags.Public);
modelBuilder.AddConfiguration(ctors[0].Invoke(new object[] { }));

这样:

modelBuilder.AddConfiguration(Activator.CreateInstance(type));

我从反射中得到的类是这样的:

public class LessonMapping : EntityTypeConfiguration<Lesson> 
public class ChapterMapping : EntityTypeConfiguration<Chapter> 
public class TrainingMapping : EntityTypeConfiguration<Training> 

两者都返回object,因此该方法不接受它们。有什么办法吗?

【问题讨论】:

  • 你必须转换Activator.CreateInstance的结果
  • 是的,但我是通过 Reflection 获取类型的,它具有通用的 TEntity,所以我不知道该怎么做
  • 创建的对象是TEntity吗?
  • @InBetween 我用这些信息更新了问题
  • @BradleyDotNET 我看不出这个问题是如何与您标记的问题重复的,除了标题。问题完全不同……

标签: c# generics reflection system.reflection


【解决方案1】:

Activator 具有用于创建具有默认构造函数的类型化实例的通用方法。看起来你有,所以使用以下内容:

var obj = Activator.CreateInstance<LessonMapping>();
modelBuilder.AddConfiguration(obj);

如果这对您不起作用或者您需要将参数传递给您的构造函数:

var obj = (LessonMapping)Activator.CreateInstance(typeof(LessonMapping));
modelBuilder.AddConfiguration(obj);

最后,如果您将此代码放在另一个泛型方法中,您可以将 LessonMapping 等替换为泛型:

void DoSomethingGeneric<TEntity>(ModelBuilder builder)
{
    var obj = Activator.CreateInstance<EntityTypeConfiguration<TEntity>>();
    modelBuilder.AddConfiguration(obj);
}

EDIT添加AddConfiguration方法的反射

对于您的 AddConfiguration 方法的非通用用法,您必须使用反射来调用它:

void DoSomethingNonGeneric(ModelBuilder builder, Type entityType)
{
    // make the generic type for the given entity type using the generic definition
    var entityConfigType = typeof(EntityTypeConfiguration<>).MakeGenericType(new Type[] { entityType });

    // instantiate the entity type configuration
    var obj = Activator.CreateInstance(entityConfigType);

    // get and make a generic method to invoke with the object above. Check the binding flags if it doesn't find the method
    var methodDef = typeof(Extensions).GetMethod("AddConfiguration", BindingFlags.Static | BindingFlags.Public);
    var method = methodDef.MakeGenericMethod(new Type[] { entityConfigType });

    // and finally the end goal
    method.Invoke(null, new object[] { builder, obj });
}

此时我的假设是:

  1. 您想要传递实体的类型(与配置相反)。如果您要传递配置类的类型,则将参数名称 entityType 更改为 entityConfigType 并删除生成泛型类型的第一行。有关应该替换第一行的类型检查代码,请参见下文。
  2. 您的扩展方法在一个名为 Extensions 的类中。
  3. 您正在检查的类型是否符合AddConfiguration(...) 的要求,因此您不会因为使用了错误的类型而出现调用异常。再次参见下面的类型检查示例,我通常会添加到此类非泛型方法中。
  4. 设置methodDef 的行按预期工作,因为我目前没有办法对其进行测试。

如果您直接传递entityConfigType 并想检查类型,请在前几行添加:

if (!CheckDerivesFrom(typeof(EntityTypeConfiguration<>), entityConfigType)
    throw new ArgumentException("entityConfigType");

并在某处创建检查方法。我确信必须有一种更简单的方法来测试一个类型是否源自给定的泛型定义,但我一直使用它,直到我得到更简单的方法。

bool CheckDerivesFrom(Type baseType, Type typeToCheck)
{
    if (!baseType.IsGenericDefinition)
        return baseType.IsAssignableFrom(typeToCheck);

    // assume typeToCheck is never a generic definition
    while (typeToCheck != null && typeToCheck != typeof(object))
    {
        if (typeToCheck.IsGenericType && baseType.IsAssignableFrom(typeToCheck.GetGenericDefinition()))
            return true;
        typeToCheck = typeToCheck.BaseType;
    }
    return false;
}

【讨论】:

  • 我通过反射获取它们,因为我有 50 个映射类,手动保持 AddConfiguration 代码同步感觉不对,这就是我尝试通过反射来实现的原因。您的解决方案可以工作,但是我必须手动将对象转换为类型,所以我最终会以同样的方式进行 50 次调用。现在我看到我应该把这个上下文放在问题中以更好地描述真正的问题..
  • 好的,那你真正想要的也是通过反射来执行配置方法,我会更新我的答案
  • 谢谢,没用,我想通过配置,所以我做了你指出的更改,我在Invoke 调用时收到以下错误:'SORTE.Persistence.Mappings.ChapterMapping' cannot be converted to type 'SORTE.Persistence.EntityTypeConfiguration1` ,但是用反射调用方法本身的想法很棒。我会尝试从这里出发
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-14
  • 1970-01-01
  • 2020-01-05
  • 1970-01-01
  • 1970-01-01
  • 2011-12-07
  • 2017-05-23
相关资源
最近更新 更多