忽略/覆盖AttributeUsage限制

Mik*_*ski 8 .net c# custom-attributes .net-core

我需要能够应用于DisplayAttribute类,但它AttributeUsage不允许在当前的.NET/.NET Core版本中.看起来这已经针对.NET Core vNext进行了补救,但如果有一些解决方法能够以某种方式忽略或覆盖此限制,直到此更改进入.NET版本非常有用.我能看到的唯一选择是重新实现整个事情(包括本地化),但我真的不想支持和测试,只要在.NET vNext发布时就弃用它.

任何聪明的想法/黑客?

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

*我似乎无法编辑赏金说明,只是为了澄清,赏金的解决方案必须适用于.NET Framework,以及.NET Core的奖励积分.

a-c*_*tor 1

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

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

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

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

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

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
{
}
Run Code Online (Sandbox Code Playgroud)

现在我们添加一个小小的 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";
  }
}
Run Code Online (Sandbox Code Playgroud)

它将 转换DisplayPatchAttribute为 a DisplayAttribute,因此程序输出“yes”。

然后,它将DisplayPatchAttribute看起来像普通属性DisplayAttribute,并将其属性复制到新属性。

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