Get access to an object from a parameter attribute in C #

This is my method

public Component SaveComponent([ValidateMetaFields] Component componentToSave) {
    ...
}

This is my custom attribute:

[AttributeUsage(AttributeTargets.Parameter)]
public class ValidateMetaFieldsAttribute : Attribute
{
    public ValidateMetaFieldsAttribute()
    {
        // how to access `componentToSave` here?
    }
}

I am wondering if there is a way to access an object componentToSavefrom ValidateMetaFieldsAttribute? I could not find any sample code or examples.

+4
source share
2 answers

No, attribute instances have no idea of ​​the purpose to which they apply.

Note that you usually extract attributes from the target, so whenever you make this selection, it can potentially provide information for the following. Potentially a little annoying, but hopefully not feasible.

One minor exception to all this is subscriber information attributes - if you use something like

[AttributeUsage(AttributeTargets.Parameter)]
public class ValidateMetaFieldsAttribute : Attribute
{
    public ValidateMetaFieldsAttribute([CallerMemberName] string member = null)
    {
        ...
    }
}

... (SaveComponent) , . , .

, , :

TotSave , .

, . , . , , .

, AOP, . PostSharp.

+16

Mono.Cecil library

:

public interface IArgumentValidator
{
    void Validate(object argument);
}

ValidateMetaFieldsAttribute :

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class ValidateMetaFieldsAttribute : Attribute, IArgumentValidator
{
    public void Validate(object argument)
    {
        //todo: put your validation logic here
    }
}

IL-Rewriter

, Mono.Cecil nuget.
:

var assembly = AssemblyDefinition.ReadAssembly(@"ClassLibrary1.dll"); // your project assembly
var module = assembly.MainModule;

IArgumentValidator -:

var validatorInterface = module.Types
  .FirstOrDefault(t => t.IsInterface && t.Name == "IArgumentValidator");
var validators = module.Types
  .Where(t => t.Interfaces.Contains(validatorInterface)).ToArray();

, :

var typesToPatch = module.Types.Select(t => new
{
    Type = t,
    Validators = 
        t.Methods.SelectMany(
         m => m.Parameters.SelectMany(
          p => p.CustomAttributes.Select(a => a.AttributeType)))
        .Distinct()
        .ToArray()
})
.Where(x => x.Validators.Any(v => validators.Contains(v)))
.ToArray();

, ( )

foreach (var typeAndValidators in typesToPatch)
{
    var type = typeAndValidators.Type;
    var newFields = new Dictionary<TypeReference, FieldDefinition>();

    const string namePrefix = "e47bc57b_"; // part of guid
    foreach (var validator in typeAndValidators.Validators)
    {
        var fieldName = $"{namePrefix}{validator.Name}";
        var fieldDefinition = new FieldDefinition(fieldName, FieldAttributes.Private, validator);
        type.Fields.Add(fieldDefinition);
        newFields.Add(validator, fieldDefinition);
    }

, . :

var initFields = new MethodDefinition($"{namePrefix}InitFields", MethodAttributes.Private, module.TypeSystem.Void);
foreach (var field in newFields)
{
    initFields.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
    initFields.Body.Instructions.Add(Instruction.Create(OpCodes.Newobj, field.Key.Resolve().GetConstructors().First()));
    initFields.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, field.Value));
}
initFields.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
type.Methods.Add(initFields);

, . , :

var ctors = type.GetConstructors().ToArray();
var rootCtors = ctors.Where(c =>
    !c.Body.Instructions.Any(i => i.OpCode == OpCodes.Call
    && ctors.Except(new []{c}).Any(c2 => c2.Equals(i.Operand)))).ToArray();
foreach (var ctor in rootCtors)
{
    var retIdx = ctor.Body.Instructions.Count - 1;
    ctor.Body.Instructions.Insert(retIdx, Instruction.Create(OpCodes.Ldarg_0));
    ctor.Body.Instructions.Insert(retIdx + 1, Instruction.Create(OpCodes.Call, initFields));
}

( rootCtors. , , , )

, , - ,

foreach (var method in type.Methods)
{
    foreach (var parameter in method.Parameters)
    {
        foreach (var attribute in parameter.CustomAttributes)
        {
            if (!validators.Contains(attribute.AttributeType))
                continue;

            var field = newFields[attribute.AttributeType];
            var validationMethod = field.FieldType.Resolve().Methods.First(m => m.Name == "Validate");
            method.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Ldarg_0));
            method.Body.Instructions.Insert(1, Instruction.Create(OpCodes.Ldfld, field));
            method.Body.Instructions.Insert(2, Instruction.Create(OpCodes.Ldarg_S, parameter));
            method.Body.Instructions.Insert(3, Instruction.Create(OpCodes.Callvirt, validationMethod));
        }
    }
}

, ,

assembly.Write("PatchedAssembly.dll");

</" > ( dotPeek)
:

public class Demo
{
    public Component SaveComponent([ValidateMetaFields] Component componentToSave)
    {
        return componentToSave;
    }
}

-:

public class Demo
{
  private ValidateMetaFieldsAttribute e47bc57b_ValidateMetaFieldsAttribute;

  public Component SaveComponent([ValidateMetaFields] Component componentToSave)
  {
    this.e47bc57b_ValidateMetaFieldsAttribute.Validate((object) componentToSave);
    return componentToSave;
  }

  public Demo()
  {
    this.e47bc57b_InitFields();
  }

  private void e47bc57b_InitFields()
  {
    this.e47bc57b_ValidateMetaFieldsAttribute = new ValidateMetaFieldsAttribute();
  }
}
+2

Source: https://habr.com/ru/post/1691792/


All Articles