忽略/覆盖AttributeUsage限制

时间:2018-06-29 06:07:19

标签: c# .net .net-core custom-attributes

我需要能够将DisplayAttribute应用于类,但是在当前.NET / .NET Core版本中,其AttributeUsage不允许这样做。看起来像这样的has been remedied for .NET Core vNext,但如果有某种变通办法可以忽略或覆盖此限制,直到此更改进入.NET版本,这将非常有用。我能看到的唯一选择是重新实现整个过程(包括本地化),但是我真的不想支持和测试它,只要在.NET vNext出现后就弃用它即可。

有什么聪明的主意/技巧吗?

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

*我似乎无法编辑赏金说明,因此只是为了澄清,针对赏金的解决方案必须适用于.NET Framework,也适用于.NET Core。

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

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

因此,我创建了一个小的Fody加载项,将其某些属性重写为DisplayAttribute

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

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

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

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编织器,将该属性重写为真实的属性(传入的恶意代码):

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,因此程序输出“是”。

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

未经.NET Core测试,但由于Fody支持net core,并且修复程序位于IL级别,因此应该可以正常工作。