Игнорировать / переопределять ограничения AttributeUsage

Мне нужно иметь возможность подать заявку DisplayAttribute на занятия, но его AttributeUsage не допускает этого в текущем выпуске.NET / .NET Core. Похоже, что это было исправлено для.NET Core vNext, но если есть какой-то обходной путь, позволяющий каким-то образом игнорировать или переопределять это ограничение, пока это изменение не попадет в выпуск.NET, который будет чрезвычайно полезен. Единственный вариант, который я вижу, это переопределение всего этого (включая локализацию), но я действительно не хочу поддерживать и тестировать его, чтобы просто устареть, как только выйдет.NET vNext.

Любые умные идеи / хаки?

Проверяются ли ограничения AttributeUsage во время выполнения CLR или они просто компилируют ограничения по времени? Если они проверяются только во время компиляции, то есть ли умный способ изменить метаданные, используемые компилятором, чтобы "обмануть" их, разрешив использование или каким-либо образом изменив системную сборку, чтобы мои машины разработчика разрешали использование?

* Кажется, я не могу отредактировать описание награды, поэтому, чтобы уточнить, решение для награды должно работать для.NET Framework, а также бонусные баллы для.NET Core.

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 всегда выводится "нет" (обратите внимание, что тестовый класс использует наш фиктивный атрибут вместо реального):

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 поддерживает сетевое ядро ​​и исправление на уровне IL, оно должно работать без проблем.

Я декомпилировал и добавил AttributeTargets.Class и перекомпилировал. Я изменил пространство имен на System.ComponentModel.MyDataAnnotations, чтобы избежать коллизий пространства имен. Если вам нужно изменить пространство имен или что-то еще, я могу отправить sln.

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

Другие вопросы по тегам