【问题标题】:Ignore / Override AttributeUsage Restrictions忽略/覆盖 AttributeUsage 限制
【发布时间】:2018-12-08 06:24:14
【问题描述】:

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

有什么聪明的想法/技巧吗?

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

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

【问题讨论】:

  • 完整的框架也不允许。有很多方法使用该属性,它们必须以一种不明显的方式进行扩展。当您选择一个与微软计划不兼容的产品时,真是太可惜了。如果他们真的做到了,那就不是什么承诺了。本地化类名,嗯,向我们展示了对它的实际需求。
  • @HansPassant 根据我问题中的链接,.NET Core 似乎已经进行了更改,所以我不确定您的意思。就用途而言,这只是一种很好且简单的方法,它允许开发人员使用友好的可本地化名称和描述来注释类,以便在为它们自动生成视图时正确显示它们。
  • @HansPassant 出于好奇,需要以不明显的方式扩展方法是什么意思。
  • System.ComponentModel 中的DisplayName 属性可以作为替代解决方案应用于类。它适用于 .Net 和 .Net Core。
  • @OL。谢谢你的建议。我已经添加了对 DisplayNameAttribute 和 DescriptionAttribute 的支持作为糟糕的替代品,但由于各种原因,它远不理想。我猜我们只是放弃并重新实现 DisplayAttribute 并将其称为 ClassDisplayAttribute 或类似的东西来获得所需的功能。

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


【解决方案1】:

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

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

【讨论】:

  • 修补和引用 .NET 程序集是有问题的,我不建议这样做。由于 .NET 程序集已签名,因此您无法在机器范围内对其进行修补。您还必须修补 GAC 才能使其正常工作。即使您自己引用 dll,您也会遇到问题,因为您的修补类型与 .NET 框架的类型不同。
  • 谢谢 - @a-ctor 是对的,这有一些问题,但 +1 用于构建具有固定属性使用的类的工作副本。
  • 我其实一开始就尝试过他的方法,但无法开始工作,但看起来他成功了!祝你好运!
【解决方案2】:

虽然您不应该更改现有的 .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”(请注意,测试类使用我们的虚拟属性而不是真实属性):

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 编织器,将属性重写为真实属性(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,因此程序输出“是”。

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

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

【讨论】:

  • 为此使用 Fody 是一个很好的解决方案。有趣的是,它实际上并没有在运行时验证属性使用情况。
猜你喜欢
  • 1970-01-01
  • 2016-05-07
  • 2020-03-30
  • 2015-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多