2013-06-12 22 views
5

我試圖用塞西爾找到來電的情況下使用了常規的測試接口的通用方法。我無法識別MethodReference中的通用類型。如何使用塞西爾找到傳遞給泛型方法的類型?

我已經建立了一個基本的測試:

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(); 
} 

所以我想找到調用farm.Add<IAnimal>(),這不會給一個編譯時錯誤甚至是運行時錯誤,直到農場可以設想通過反思來創建一個類型的實例。我的實際使用案例是DI容器的常規測試。

塞西爾進來給它的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部分就是我未能識別主叫Farm::Add時使用的泛型類型。具體而言,MethodReferenceGenericParameters屬性是空的,所以GenericParameters[0]給出一個ArgumentOutOfRangeException。我已經探討了MethodReference,和我肯定得到調用Farm::Add,但我看不到任何地方,涉及到泛型類型所使用除FullName屬性,它是沒有用的。

我怎樣才能得到塞西爾識別在呼叫中的泛型類型?

回答

3

如果我投的MethodReferenceGenericInstanceMethodGenericArguments參數做什麼,我需要:

var callsInError = testCases 
    .Where(test => !((GenericInstanceMethod)test).GenericArguments[0].Resolve().IsClass) 
    ; 
相關問題