2013-06-12 5 views
5

私はCecilを使用して、コンベンションテスト用のインターフェイスを使用して汎用メソッドへの呼び出しのインスタンスを見つけようとしています。 MethodReferenceからジェネリックタイプを特定できません。農場まで、ジェネリックメソッドに渡される型を見つけるためにCecilを使うにはどうすればいいですか?

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()方法でそれに入って来:私はFarm::Addを呼び出すときに使用される一般的なタイプを識別するために失敗していますどこ

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一部です。具体的には、MethodReferenceGenericParametersプロパティは空です。したがって、GenericParameters[0]ArgumentOutOfRangeExceptionとなります。私はMethodReferenceを調べましたが、私は確かにFarm::Addへの呼び出しを受けていますが、便利ではないFullNameプロパティを除き、使用されているジェネリックタイプに関連するものはどこにもありません。

コールで使用されるジェネリックタイプを識別するためにCecilを取得するにはどうすればよいですか?

答えて

3

私はGenericInstanceMethodGenericArgumentsパラメータにMethodReferenceをキャストしている場合は、私が必要なものを行います。

var callsInError = testCases 
    .Where(test => !((GenericInstanceMethod)test).GenericArguments[0].Resolve().IsClass) 
    ; 
関連する問題