Encontre os valores dos argumentos de chamada de método em IL
-
21-08-2019 - |
Pergunta
Eu tenho vários métodos especiais, e eu quero analisar se chama em compilado montagem.
Exemplo:
public static class SrcHelper {
[MySpecialMethod]
[Conditional( "DEBUG" )]
public static void ToDo( params object[] info ) {
/* do nothing */
/* this method is not called when code is compiled in RELEASE mode */
}
}
// ... somewhere else in another assembly ...
Array CreateArraySampleMethod( int size ) {
// This call has only informative character. No functionality is required.
SrcHelper.ToDo( "Should create array of ", typeof( MyClass ), " with specified size." );
throw new NotImplementedException();
}
A partir deste código compilado Quero obter os valores de argumento { "deverá criar variedade de" MyClass "com o tamanho especificado." }. Tentei uso Cecil de Mono, e eu achei as instruções para a chamada de método "todo". Mas agora estou confuso como identificar instrução com valores de argumento.
Eu sei, pode haver situação complexa, e o valor de algum argumento não pode ser resolvido. Mas eu preciso resolver apenas valores constantes. - É o suficiente para o meu propósito
Graças.
EDIT: O método de "todo" (e similares) deve ser usado como alternativa para comentários (//, / * ... * /), e após a compilação, deve ser IL analisado e gerado automaticamente documentação e Todo-lista para betão de montagem.
Solução
A geração de código é um pouco confuso, mas pode ser feito para casos simples:
compilação:
public static void Main(string[] args)
{
Console.WriteLine("", // ignore this argument
"Should create array of ", typeof(int), " with specified size." "x");
}
(adicionando "x" para forçá-lo a usar uma sobrecarga params)
dá
.method public hidebysig static void Main(string[] args) cil managed
{
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
.maxstack 4
.locals init (
[0] object[] objArray)
L_0000: ldstr ""
L_0005: ldc.i4.4
L_0006: newarr object
L_000b: stloc.0
L_000c: ldloc.0
L_000d: ldc.i4.0
L_000e: ldstr "Should create array of "
L_0013: stelem.ref
L_0014: ldloc.0
L_0015: ldc.i4.1
L_0016: ldtoken int32
L_001b: call class [mscorlib]System.Type
[mscorlib]System.Type::GetTypeFromHandle(
valuetype [mscorlib]System.RuntimeTypeHandle)
L_0020: stelem.ref
L_0021: ldloc.0
L_0022: ldc.i4.2
L_0023: ldstr " with specified size."
L_0028: stelem.ref
L_0029: ldloc.0
L_002a: ldc.i4.3
L_002b: ldstr "x"
L_0030: stelem.ref
L_0031: ldloc.0
L_0032: call void [mscorlib]System.Console::WriteLine(string, object[])
L_0037: ret
}
Assim que você tem a fazer é analisar o il para detectar os argumentos sendo empurrado para a matriz de compilador gerado. um heristic que é frágil, mas pode ser suficiente dizer:
- encontrar chamada para 'meu método'.
- encontrar mais próximo objeto newarr anterior
- tomar todas ldstr e ldtoken entre estes e assumir que eles são os argumentos.
Esta é duro, mas pode ser suficiente para suas necessidades.
abordagem de estilo Um AOP vai te dar o que você quer em tempo de execução , simplesmente instrumentação cada chamada para despejar os valores, mas no momento sompile a abordagem acima é a sua única opção realista, dada somente a IL.
O código gerado pode ser muito diferente nas compilações, Você não será capaz de detectar a auto série gerado versos alguém criá-lo explicitamente si (que pode ser mais longe do site de chamada ou até mesmo em um diferente método / construtor / aula .
cláusula
Devo observar após a sua edição por que você quer fazer isso esse atributo com base anotações são uma solução muito melhor, não vejo por que você iria querer fazer isso no método quando você pode atribuí-lo diretamente ...
Outras dicas
Eu não tenho certeza que você quer dizer. No entanto, aviso que a sua função realmente só fica um argumento: um array. Isso é o que você recebe em IL, também. Dentro a função, você pode percorrer a matriz para obter seus valores:
public static void ToDo( params object[] info ) {
foreach (object x in info)
Console.WriteLine(x);
}