2013-06-12 13 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>() करने के लिए कॉल, जो एक संकलन समय त्रुटि या यहां तक ​​कि एक रन-टाइम त्रुटि नहीं देंगे खोजना चाहते हैं, खेत तक कल्पनाशील ने प्रतिबिंब के माध्यम से प्रकार का एक उदाहरण बनाने की कोशिश की। मेरा वास्तविक उपयोग केस एक डीआई कंटेनर के लिए एक सम्मेलन परीक्षण है।

सेसिल 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 बुला इस्तेमाल किया प्रकार की पहचान करने में नाकाम रहने रहा हूँ। विशेष रूप से, GenericParametersMethodReference की संपत्ति खाली है, इसलिए GenericParameters[0]ArgumentOutOfRangeException देता है। मैंने MethodReference की खोज की है, और मुझे निश्चित रूप से Farm::Add पर कॉल प्राप्त हो रही है, लेकिन मैं FullName संपत्ति को छोड़कर उपयोग किए जाने वाले सामान्य प्रकार से संबंधित कहीं भी नहीं देख सकता, जो उपयोगी नहीं है।

कॉल में इस्तेमाल किए गए सामान्य प्रकार की पहचान करने के लिए मुझे सेसिल कैसे मिल सकता है?

उत्तर

3

अगर मैं एक GenericInstanceMethodGenericArguments पैरामीटर के लिए MethodReference डाली कि मैं क्या जरूरत है:

var callsInError = testCases 
    .Where(test => !((GenericInstanceMethod)test).GenericArguments[0].Resolve().IsClass) 
    ; 
संबंधित मुद्दे

 संबंधित मुद्दे