将PropertyInfo转换为泛型类型

时间:2011-06-10 09:50:17

标签: c# generics reflection casting

我有以下课程:

public class AuthContext : DbContext
{
    public DbSet<Models.Permission> Permissions { get; set; }
    public DbSet<Models.Application> Applications { get; set; }
    public DbSet<Models.Employee> Employees { get; set; } 
    // ...
}

我为类型Clear()创建了扩展方法DbSet<T>。使用反射我能够检查AuthContext的实例,并将DbSet<T>类型的所有属性读作PropertyInfo[]。如何将PropertyInfo转换为DbSet<T>以便在其上调用扩展方法?

var currentContext = new AuthContext();
...
var dbSets = typeof(AuthContext).GetProperties(BindingFlags.Public | BindingFlags.Instance);
dbSets.Where(pi =>
                pi.PropertyType.IsGenericTypeDefinition &&
                pi.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)).ToList()
      .ForEach(pi = ((DbSet<T>)pi.GetValue(currentContext, null)).Clear()); // !!!THIS WILL NOT WORK

4 个答案:

答案 0 :(得分:4)

请参阅Andras Zoltan的答案,解释你做错了什么。

但是,如果您使用.NET 4.0,则无需使用反射来调用该方法,您只需使用新的dynamic关键字:

var currentContext = new AuthContext();
var dbSets = typeof(AuthContext).GetProperties(BindingFlags.Public | 
                                               BindingFlags.Instance);
dbSets.Where(pi => pi.PropertyType.IsGenericType &&
                   pi.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
      .ToList()
      .ForEach(pi => ExtensionClass.Clear((dynamic)pi.GetValue(currentContext, 
                                                               null)));

我将演员从DbSet<T>更改为dynamic并更改了调用方法的方式 由于Clear是一种扩展方法,因此无法直接在dynamic类型上调用,因为dynamic不了解扩展方法。但由于扩展方法不仅仅是静态方法,因此您始终可以将对扩展方法的调用更改为对静态方法的正常调用。
您需要做的就是将ExtensionClass更改为定义Clear的真实班级名称。

答案 1 :(得分:1)

你的演员是错的。

您无法转换为(DbSet<T>),因为除非在通用方法或泛型类型中定义T,否则这不是具体类型。

你有几种可能性。

如果DbSet有一个基类(例如我的代码中的DbSet_BaseClass),您仍然可以从中实现Clear()方法 - 然后从以下位置更改它的签名:

public static void Clear<T>(this DbSet<T>)

为:

public static void Clear(this DbSet_BaseClass)

然后,您可以在.ForEach更改为((DbSet_BaseClass)pi.GetValue...

如果你不能这样做,你可以通过为Clear的{​​{1}}构建一个特定的通用版本来反映调用T扩展方法:

DbSet<T>

然后,给定一个属性信息和上下文实例:

MethodInfo myClearMethod = typeof(container_type).GetMethod(
  "Clear", BindingFlags.Public | BindingFlags.Static);

您可以在此基础上进行大量优化,缓存代理等等,但这样可行。

更新

或者看看@Daniel Hilgarth对方式的回答,即动态地将调用分配给扩展方法而无需执行上述任何操作(动态调度有效地执行上述操作,但对于您而言)所有缓存都在上面)。如果是我 - 我会用它。

答案 2 :(得分:0)

你不能投射类型,因为它们之间没有任何关系。你得到一个PropertyInfo来告诉你类型,但不是类型本身。

我认为您将要使用Type.GetMethod来定位“Clear”方法,作为MethodInfo,然后您将能够调用MethodInfo.Invoke。

答案 3 :(得分:0)

您必须对DbSet进行反射以调用“清除方法”

试试这个:

var dbSets = typeof(AuthContext).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            dbSets.Where(pi =>
                            pi.PropertyType.IsGenericType &&
                            pi.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)).ToList()
                  .ForEach(pi =>
                      {
                          typeof(DbSet<>)
                              .MakeGenericType(pi.PropertyType.GetGenericArguments()[0])
                              .GetMethod("Clear")
                              .Invoke(pi.GetValue(currentContext, null), null);
                      }
                      );
相关问题