忽略/覆盖属性使用限制



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

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

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

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

虽然您不应该更改现有的 .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 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 支持网络核心并且修复程序在 IL 级别,因此它应该可以正常工作。

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

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

最新更新