2013-06-12 11 views
5

Próbuję użyć Cecil, aby znaleźć wystąpienia wywołań do ogólnej metody za pomocą interfejsu do testu konwencji. Mam problem z identyfikacją typu ogólnego z MethodReference.Jak mogę użyć Cecil, aby znaleźć typ przekazany do ogólnej metody?

Wcześniej skonfigurować podstawowy test:

private interface IAnimal 
{ 
} 

private class Duck : IAnimal 
{ 
} 

private class Farm 
{ 
    private readonly ICollection<string> _animals = new List<string>(); 

    public void Add<T>() 
    { 
     _animals.Add(typeof(T).Name); 
    } 

    public override string ToString() 
    { 
     return string.Join(", ", _animals); 
    } 
} 

static Farm FarmFactory() 
{ 
    var farm = new Farm(); 
    farm.Add<Duck>(); 
    farm.Add<Duck>(); 
    farm.Add<IAnimal>(); // whoops 
    farm.Add<Duck>(); 
    return farm; 
} 

private static void Main(string[] args) 
{ 
    var farm = FarmFactory(); 
    Console.WriteLine("Farm:"); 
    Console.WriteLine(farm); 

    // Use Cecil to find the call to farm.Add<IAnimal>(): 
    Console.WriteLine("Errors:"); 
    FindErrors(); 
    Console.Read(); 
} 

więc chcę znaleźć wezwanie do farm.Add<IAnimal>(), które nie dadzą się błąd czasu kompilacji lub nawet błąd czasu, aż w gospodarstwie wyobrażalne próbowałem stworzyć instancję typu poprzez odbicie. Moim faktycznym przypadkiem użycia jest test konwencji dla kontenera DI.

Cecil przychodzi do niego w sposób FindErrors():

private static void FindErrors() 
{ 
    var methods = AssemblyDefinition.ReadAssembly(typeof (Farm).Assembly.Location) 
            .Modules 
            .SelectMany(module => module.Types) 
            .SelectMany(type => type.Methods) 
            .Where(method => method.HasBody) 
            .ToArray() 
     ; 
    var callsToFarmDotAdd = methods 
     .Select(method => new 
      { 
       Name = method.Name, 
       MethodReferences = GetCallsToFarmDotAdd(method) 
      }) 
     .Where(x => x.MethodReferences.Any()) 
     .ToArray() 
     ; 
    var testCases = callsToFarmDotAdd 
     .SelectMany(x => x.MethodReferences) 
     ; 
    var callsInError = testCases 
     .Where(test => !test.GenericParameters[0].Resolve().IsClass) 
     ; 

    foreach (var error in callsInError) 
    { 
     Console.WriteLine(error.FullName); 
    } 
} 

private static IEnumerable<MethodReference> GetCallsToFarmDotAdd(MethodDefinition method) 
{ 
    return method.Body.Instructions 
       .Where(instruction => instruction.OpCode == OpCodes.Callvirt) 
       .Select(instruction => (MethodReference) instruction.Operand) 
       .Where(methodReference => methodReference.FullName.Contains("Farm::Add")) 
     ; 
} 

callsInError część to gdzie ja nie wskazując typ rodzajowy używane podczas wywoływania Farm::Add. W szczególności właściwość GenericParameters z MethodReference jest pusta, więc GenericParameters[0] daje . Zbadałem numer MethodReference i na pewno otrzymuję wywołania do Farm::Add, ale nie widzę nigdzie, które odnosi się do typowego typu, z wyjątkiem właściwości FullName, która nie jest użyteczna.

Jak mogę uzyskać Cecil w celu zidentyfikowania ogólnego rodzaju używanego w połączeniu?

Odpowiedz

3

Gdybym rzucił MethodReference do GenericInstanceMethod parametr GenericArguments robi to, czego potrzebuję:

var callsInError = testCases 
    .Where(test => !((GenericInstanceMethod)test).GenericArguments[0].Resolve().IsClass) 
    ; 
Powiązane problemy