忽略/覆盖 AttributeUsage 限制

Ignore / Override AttributeUsage Restrictions

我需要能够应用 DisplayAttribute to classes but its AttributeUsage doesn't allow that in the current .NET / .NET Core release. It looks like this has been remedied for .NET Core vNext,但如果有一些解决方法能够以某种方式忽略或覆盖此限制,直到此更改进入 .NET 版本,这将非常有用。我能看到的唯一选择是重新实现整个东西(包括本地化),但我真的不想支持和测试它只是为了在 .NET vNext 出现后立即弃用它。

有聪明的ideas/hacks吗?

AttributeUsage 限制是在运行时由 CLR 验证还是只是编译时限制?如果它们仅在编译时检查,那么是否有一种聪明的方法可以将编译器使用的元数据更改为 "trick" 允许使用或以某种方式修改系统程序集以便我的开发机器允许使用?

*我似乎无法编辑赏金描述,所以只是为了澄清一下,赏金解决方案必须适用于 .NET Framework,奖励积分也适用于 .NET Core。

我反编译并添加了AttributeTargets.Class并重新编译。 我将命名空间更改为 System.ComponentModel.MyDataAnnotations 以避免命名空间冲突。 如果您需要改回名称空间或其他东西,我可以发送 sln。

https://drive.google.com/open?id=1KR5OJwsOtGUdOBWIxBoXuDHuq4Nw-X7d

虽然您不应该更改现有的 .NET 程序集 - 由于签名和 GAC(麻烦等待),可以在编译后将属性添加到现有的 class 并且它可以正常工作。 AttributeUsage 似乎没有在运行时强制执行。

所以我创建了一个小的 Fody 插件,将某个属性重写到 DisplayAttribute:

首先是我们将通过 Fody 重写的小虚拟属性:

[AttributeUsage (AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Class)]
public class DisplayPatchAttribute : Attribute
{
  public DisplayPatchAttribute()
  {
  }
}

还有一个小虚拟程序,用于测试 DisplayAttribute 是否应用于测试 class。当 运行 没有 Fody-addin 它总是打印 "no" (注意测试 class 使用我们的虚拟属性而不是真实的):

internal static class Program
{
  private static void Main (string[] args)
  {
    var attr = Attribute.GetCustomAttribute (typeof(Test), typeof(DisplayAttribute)) as DisplayAttribute;
    Console.WriteLine (attr == null ? "no" : "yes");
  }
}

[DisplayPatch]
internal class Test
{
}

现在我们添加一个小的 Fody weaver,将属性重写为真实的属性(hacky 代码传入):

public class DisplayAttributeWeaver : BaseModuleWeaver
{
  public override void Execute()
  {
    var dataAnnotationAssembly = ModuleDefinition.AssemblyReferences.First (e => e.Name.Contains ("DataAnnotation"));
    var resolvedDataAnnotationAssembly = ModuleDefinition.AssemblyResolver.Resolve (dataAnnotationAssembly);
    var displayAttribute = resolvedDataAnnotationAssembly.Modules.First().GetType ("System.ComponentModel.DataAnnotations.DisplayAttribute");
    var displayAttributeConstructor = ModuleDefinition.ImportReference(displayAttribute.GetConstructors().First());

    foreach (var type in ModuleDefinition.Types)
    {
      var targetAttribute = type.CustomAttributes.FirstOrDefault (e => e.AttributeType.Name == "DisplayPatchAttribute");
      if (targetAttribute == null)
        continue;

      type.CustomAttributes.Remove (targetAttribute);

      var newAttr = new CustomAttribute (displayAttributeConstructor);
      type.CustomAttributes.Add (newAttr);
    }
  }

  public override IEnumerable<string> GetAssembliesForScanning()
  {
    yield return "mscorlib";
    yield return "System";
  }
}

它将 DisplayPatchAttribute 转换为 DisplayAttribute 因此程序输出 "yes".

然后 DisplayPatchAttribute 看起来像正常的 DisplayAttribute 并将其属性复制到新属性。

未针对 .NET Core 进行测试,但由于 Fody 支持 net core 并且修复是在 IL 级别,它应该可以正常工作。