No i proszę, jak już coś wymyślę to się okazuje, że większość o tym wie :) Dużo kudos dla Pawła, oczywiście chodziło mi komendę:

ildasm TestBox.exe  /text | findstr box

boxing_04

Jest to chyba najszybszy sposób uzyskania informacji o boxingu i unboxingu w kodzie, a staje się on jeszcze szybszy gdy zastosujemy rozszerzenie PowerCommands, wtedy wystarczy jedynie kliknąć na projekcie wybrać Open Command Prompt

boxing_01

i następnie przejść do katalogu bin/debug|release gdzie już możemy spokojnie operować na pliku – oczywiście dla chcących nie powinno być problemu wprowadzenie bin/debug/TestBox.exe zamiast przechodzić do katalogu.

Plus również dla FiDO, jednakże sparsowane tak wyniku zapytania by zwróciło nam zarówno tekst jak i linijkę nad nią/pod nią będzie już bardziej pracochłonne.

Myślałem, że padną także inne rozwiązania, a więc po krótce kilka słów o tych o których ja słyszałem.

nDepend

Posiadając nDepend jesteśmy wstanie dość szybko znaleźć metodę lub typ (i tylko i wyłącznie metodę lub typ), który zawiera w sobie boxing i/lub unboxing. Można to zrobić bardzo prostym zapytaniem:

boxing_02

boxing_03

Liczba linii to liczba linii w danej metodzie/w danym typie.

Mono.Cecil

Mono Cecil umożliwia nam odczytanie IL z skompilowanych już plików (jak i manipulację nim – wstawianie nowych komend, kasowanie istniejących itp) i w naszym przypadku wystarczy wykonać taki kod:

using System;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace TestBox
{
    class Program
    {
        static void Main()
        {
            AssemblyDefinition ad = AssemblyDefinition.ReadAssembly(
                typeof(Program).Assembly.Location
            );
            
            var instructions = from t in ad.MainModule.Types
                                  from m in t.Methods
                                  from i in m.Body.Instructions
                                  where i.OpCode == OpCodes.Box
                                       || i.OpCode == OpCodes.Unbox_Any
                                       || i.OpCode == OpCodes.Unbox
                                  select i;

            foreach(var instruction in instructions)
            {
                Console.WriteLine(instruction);
            }

            Console.ReadLine();
        }
    }
}

Jest to wersja uproszczona :) i wygląda prawie tak samo jak z ILDASM /text:

boxing_05

Możemy za to się pobawić trochę Cecil i na przykład wykorzystać dane z pliku PDB by pokazać także i plik źródłowy jak i linię wystąpienia błędu:

boxing_06

A tutaj kodzik to wykonujący:

using System;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Pdb;

namespace TestBox
{
    class Program
    {
        static void Main()
        {
            ISymbolReaderProvider readerProvider = new PdbReaderProvider();
            ReaderParameters readerParameters = new ReaderParameters(ReadingMode.Immediate)
            {
                SymbolReaderProvider = readerProvider
            };
            AssemblyDefinition ad = AssemblyDefinition.ReadAssembly(
                typeof(Program).Assembly.Location, readerParameters
            );

            Action<Instruction> print = instr =>
            {
                if(instr == null)
                    return;

                if(instr.Previous == null)
                    return;

                if(instr.Previous.SequencePoint == null)
                    return;

                if(instr.Previous.SequencePoint.Document == null)
                    return;

                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("ttIL:t{0}", instr);
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("ttFile:t{0}", instr.Previous.SequencePoint.Document.Url);
                Console.WriteLine("ttLine:t{0}", instr.Previous.SequencePoint.StartLine);
                Console.WriteLine();
                Console.ResetColor();
            };

            foreach(var typeDefinition in ad.MainModule.Types)
            {
                // for test purpose only, i'm loading my executable assembly
                // and i don't want to check if cecil code contains
                // boxing and unboxing
                if(!typeDefinition.Namespace.Contains("Boxing"))
                    continue;

                foreach(var methodDefinition in typeDefinition.Methods)
                {
                    var body = methodDefinition.Body;

                    Console.WriteLine("{0}.{1}", typeDefinition.Namespace, typeDefinition.Name);
                    Console.WriteLine("t" + methodDefinition.Name);

                    var boxWhere = from i in body.Instructions
                                   where i.OpCode == OpCodes.Box
                                   select i;

                    var unboxWhere = from i in body.Instructions
                                     where i.OpCode == OpCodes.Unbox_Any
                                          || i.OpCode == OpCodes.Unbox
                                     select i;

                    foreach(var instruction in boxWhere)
                    {
                        print(instruction);
                    }

                    if(boxWhere.Count() == 0)
                    {
                        Console.ForegroundColor = ConsoleColor.White;
                        Console.WriteLine("ttNo boxing...");
                        Console.ResetColor();
                    }

                    foreach(var instruction in unboxWhere)
                    {
                        print(instruction);
                    }

                    if(unboxWhere.Count() == 0)
                    {
                        Console.ForegroundColor = ConsoleColor.White;
                        Console.WriteLine("ttNo unboxing...");
                        Console.ResetColor();
                    }

                    Console.WriteLine("{0}---------------{0}", Environment.NewLine);
                }
            }

            Console.ReadLine();
        }
    }
}

ILDASM/.NET Reflector

Do przejrzenia boxingu i unboxingu z poziomu UI, możemy wykorzystać .NET Reflector lub po raz kolejny ILDASM:

boxing_07

boxing_08

Podsumowanie

Czy są to wszystkie metody? Na pewno nie, ale są to te, które przychodzą mi na myśl. Można by się jeszcze pobawić CCI Metadata API, które też umożliwia wczytywanie plików PDB – tak naprawdę Cecil z niego korzysta.

Jeżeli znacie inne metody to podzielcie się nimi proszę :) dzięki