【问题标题】:Check via Reflection if Property is computed如果计算了属性,则通过反射检查
【发布时间】:2017-02-03 17:27:18
【问题描述】:

请想象在一个类中有以下两个属性:

public string Category { get; set; }
public string DisplayCategory => "SomeCategory"

现在我只想收集所有未计算属性本身的 PropertyInfo 对象

var properties = type.GetProperties();
var serializables = properties.Where(p => p.CanRead, true));

我如何通过Reflection 查明某个属性是否为计算属性,以便我可以忽略它?

我想这样做的原因是因为我使用Expression Trees 自动创建通过Entity Framework 6 处理的查询。实体框架只为非计算属性创建列,因此无法查询计算属性。

This Article

【问题讨论】:

  • 究竟什么算作“计算”?任何不是编译时常量的东西?如果它类似于读取一个不是常量但只初始化一次的静态只读字段呢?
  • @Jon 每个 getter 背后都有某种逻辑,而不是表现得像一个字段。我只想要剩余的Properties 可以通过Entity Framework 映射,也可以使用Expression Framework 进行查询
  • 什么算作“某种逻辑”?两个都检索相同字段的属性是否会被视为未计算,即使它们反映了单个状态? getter 只是读取一个字段,但 setter 执行验证的属性呢? getter 读取字段然后从中获取属性的属性怎么样,例如public int FooLength => foo.Length;?一个只读取另一个属性而不是字段的属性呢?这里有各种可能的微妙之处......
  • 为什么 EF 不应该处理具有“复杂”主体的属性?我想只要它是一个属性(最终用属性装饰,我不是 EF 专家)EF 可以处理它,不是吗?
  • 这两个属性应该匹配哪个?如果两者都可以提供一个应该匹配的属性示例?

标签: c# reflection properties


【解决方案1】:

我使用DelegateDecompiler,目前我必须手动将每个计算的属性名称添加到自定义配置中。

如果下面的 ShouldDecompile 方法可以自动确定哪些属性应该被反编译就好了:

  public class CustomDelegateDecompilerConfiguration : Configuration {
    public static CustomDelegateDecompilerConfiguration Instance { get; } = new CustomDelegateDecompilerConfiguration();
    public static void Enable() => Configuration.Configure(Instance);

    public CustomDelegateDecompilerConfiguration() {
      RegisterDecompileableMember<Person, string>(x => x.Name);

      RegisterDecompileableMembers(typeof(string), nameof(string.IsNullOrWhiteSpace));

      RegisterDecompileableMembers(typeof(CustomComputedMethods), new[] {
        nameof(CustomComputedMethods.PersonName),
        nameof(CustomComputedMethods.MonthInteger),
        nameof(CustomComputedMethods.WholeMonthsBetween),
        nameof(CustomComputedMethods.WholeYearsBetween)
      });

    }

    public HashSet<MemberInfo> DecompileableMembers { get; } = new HashSet<MemberInfo>();

    public override bool ShouldDecompile(MemberInfo memberInfo) => memberInfo.GetCustomAttributes(typeof(DecompileAttribute), true).Length > 0
      || memberInfo.GetCustomAttributes(typeof(ComputedAttribute), true).Length > 0
      || memberInfo.GetCustomAttributes(typeof(CompilerGeneratedAttribute), true).Length > 0
      || DecompileableMembers.Contains(memberInfo)
      //***  Would be nice if auto detection was possible with non-existing methods below ***
      //|| memberInfo.AutoProperty      (One with a backing field automatically generated by the compiler)
      //|| memberInfo.HasExpressionBody (One implemented using the => (lambda) syntax)
      //|| memberInfo.HasFunctionBody   (One implemented using the normal {...} syntax)
      ;

    public override void RegisterDecompileableMember(MemberInfo prop) => DecompileableMembers.Add(prop);

    public void RegisterDecompileableMember<T, TResult>(Expression<Func<T, TResult>> expression) where T : class => RegisterDecompileableMember(expression.Body.GetMemberInfo());

    public void RegisterDecompileableMembers(Type type, params string[] memberNames) {
      foreach(var tmi in type.GetMembers().Where(mi => memberNames.Contains(mi.Name))) {
        DecompileableMembers.Add(tmi);
      }
    }

    public void RegisterDecompileableMembers<T>(params string[] memberNames) where T : class => RegisterDecompileableMembers(typeof(T), memberNames);

  }

一个示例类:

  public class Person {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string AlternativeFirstName { get; set; }

    public string Name => string.Concat(AlternativeFirstName == string.Empty ? FirstName : AlternativeFirstName, " ", LastName);
  }

一些示例扩展方法:

  public static class CustomComputedMethods {
    public static string PersonName(string firstName, string lastName, string knownAs) => (knownAs ?? firstName).Trim() + " " + lastName.Trim();
    public static long MonthInteger(this DateTime d) => checked(d.Year * 12 + d.Month);
    public static int WholeMonthsBetween(this DateTime d, DateTime maxDate) => (int)(maxDate.MonthInteger() - d.MonthInteger() - (d.Day > maxDate.Day ? 1 : 0));
    public static int WholeYearsBetween(this DateTime d, DateTime maxDate) => d.WholeMonthsBetween(maxDate) / 12;
  }

【讨论】:

    猜你喜欢
    • 2011-09-29
    • 2012-08-31
    • 1970-01-01
    • 2016-07-11
    • 1970-01-01
    • 1970-01-01
    • 2016-01-24
    • 2023-03-31
    • 2023-04-02
    相关资源
    最近更新 更多