2016-01-19 14 views
5

Uso Mono.Cecil per creare un nuovo tipo di attributo personalizzato e quindi aggiungerlo a un tipo esistente.Il membro è dichiarato in un altro modulo e deve essere importato

Per dimostrarlo, ho una DLL preesistente chiamata "Sample" con un tipo chiamato "SampleType".

Voglio usare Mono.Cecil per tessere in un nuovo tipo in "Sample" chiamato "NewAttribute" e quindi aggiungere questo attributo a "SampleType".

Il codice simile a questo: (non esattamente, ma la sua buona abbastanza per esempio)

static void AddCustomeAttribute() 
{ 
    var module = ModuleDefinition.ReadModule(AssemblyName); 
    var attrType = NewAttributeProvider.Add(module); 
    var ctor = attrType.GetConstructors().First(); 
    //module.Import(ctor); 
    CustomAttribute attribute = new CustomAttribute(ctor); 
    attribute.ConstructorArguments.Add(new CustomAttributeArgument(module.TypeSystem.String, "InternalClass")); 
    module.CustomAttributes.Add(attribute); 
    module.Write(AssemblyName); //error 
} 

-

public static TypeDefinition Add(ModuleDefinition targetModule) 
{ 
    var type = targetModule.AddType("Namespace", "NewAttribute", TypeAttributes.Public | TypeAttributes.Class, targetModule.Import(typeof(Attribute))); 
    var stringType = targetModule.TypeSystem.String; 
    var nameField = type.AddField(stringType, "_name"); 
    var nameProp = type.AddSimpleProperty(stringType, "Name", nameField); 

    // generate a constructor body 
    var constructor = type.AddConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, targetModule.TypeSystem.Void, new[] { stringType }); 
    constructor.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); 
    constructor.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1)); 
    constructor.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, nameField)); 
    constructor.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); 

    var attrUsageType = targetModule.Import(typeof(AttributeUsageAttribute)).Resolve(); 
    //var att = targetModule.Import(typeof(AttributeUsageAttribute)); 
    //targetModule.Import(attrUsageType); 
    var attributeTargetsType = targetModule.Import(typeof(AttributeTargets)); 
    //targetModule.Import(attributeTargetsType); 
    var propertiesToSet = new Dictionary<string, Tuple<TypeReference, object>> 
    { 
     {"AllowMultiple", Tuple.Create(targetModule.TypeSystem.Boolean, (object)true)} 
    }; 
    var usageAttr = type.AddCustomAttribute(attrUsageType, new[] { attributeTargetsType }, propertiesToSet); 
    //targetModule.Import(usageAttr.AttributeType); 
    targetModule.Types.Add(type); 
    return type; 
} 

-

public static CustomAttribute AddCustomAttribute(this TypeDefinition type, TypeDefinition attrType, TypeReference[] ctorParameters, Dictionary<string, Tuple<TypeReference, object>> propetiesToSet) 
{ 
    var attrUsageCtor = attrType.GetConstructors().Single(ctor => ctor.Parameters.Count == ctorParameters.Length && ValidateParameters(ctor.Parameters, ctorParameters)); 
    type.Module.Import(attrUsageCtor); 
    Collection<CustomAttributeNamedArgument> properties = new Collection<CustomAttributeNamedArgument>(); 
    foreach (KeyValuePair<string, Tuple<TypeReference, object>> typeReference in propetiesToSet) 
    { 
     properties.Add(new CustomAttributeNamedArgument(typeReference.Key, new CustomAttributeArgument(typeReference.Value.Item1, typeReference.Value.Item2))); 
    } 
    var customeAttr = new CustomAttribute(attrUsageCtor); 
    foreach (var property in properties) 
    { 
     customeAttr.Properties.Add(property); 
    } 
    type.CustomAttributes.Add(customeAttr); 
    return customeAttr; 
} 

Come si vede, i commenti in il codice sono tentativi che ho fatto per risolvere il problema ma senza successo. Sono sicuro che mi manca qualcosa, ma io non so cosa ..

risposta

10

I metodi di importazione nella Cecil avere la seguente firma:

TypeReference Import(TypeReference type) 
MethodReference Import(MethodReference method) 

Import prende un tipo o un metodo, non importa dove sono definiti, e creare un riferimento per loro per il modulo corrente. Se non usi ciò che restituiscono, il tuo codice non è corretto.

Per esempio, si scrive:

var attrUsageCtor = attrType.GetConstructors().Single(ctor => ...); 
type.Module.Import(attrUsageCtor); 

In tal caso, si sta creando un CustomAttribute per voi modulo con il costruttore definito in mscorlib. È necessario creare un riferimento per il costruttore nel modulo e utilizzarlo: il risultato di Import è ciò che è necessario utilizzare quando si crea l'attributo personalizzato.

Ti suggerisco di esaminare tutto l'utilizzo di Import e verificare che

+0

Grazie! Questo ha risolto anche il mio problema. La seguente frase è ciò che è importante "il risultato di Import è ciò che è necessario utilizzare quando si crea l'attributo personalizzato". – m1o2