MSDN.WhiteKnight - Stack Overflow answers
Ответ на "Рефлексия в c#. Как получить исходный код метода?"
Answer 1005282
С помощью рефлексии нельзя получить исходный код метода, так как рефлексия возвращает только ту информацию, которая есть в скомпилированной сборке. Рефлексия может дать IL-байткод метода, но не исходный код, ей неоткуда взять его. Исходный код нужно получать из каких-то других источников:
1) Отладочные символы
Если получение исходного кода необходимо только на машине, где ведется разработка, можно воспользоваться отладочными символами. В PDB-файлах, генерируемых компилятором, хранится информация о соответствии строк файлов исходного кода инструкциям в IL-байткоде (именно благодаря этому отладчик может при возникновении исключения может указать на конкретную строку). Определив положение начала и конца метода, считать его код из исходников - дело техники. В символах присутствует каждый метод, который физически есть в сборке, в том числе и анонимные, поэтому решение вполне применимо и для условий из лямбда-выражений.
Так как символы ссылаются на исходники по абсолютным путям, чтобы он сработал на других машинах, проект понадобится перенести на них в точности по такому же пути, как он был расположен на машине разработчика. Естественно, необязательно распространять проект целиком - главное, чтобы файл с нужным методом был доступен.
Формат PDB не документирован, однако существует несколько библиотек с открытым исходным кодом от Microsoft для работы с ним, например Microsoft.Diagnostics.Runtime (в версии 2.0 библиотеки API для чтения PDB был удален, поэтому следует использовать версию 1.Х).
2) Декомпиляция
Если исходники недоступны или нет символов, можно воспользоваться одним из декомпиляторов, чтобы восстановить их из байткода на лету. Например, библиотека ICSharpCode.Decompiler, движок декомпилятора ILSpy, поддерживает декомпиляцию типа в синтаксическое дерево, которое можно обойти и найти нужный метод. В том числе, с помощью нее программа можно спокойно декомпилировать саму себя! Правда, чтобы корректно обрабатывать лямбды, придется отключить подстановку кода анонимных методов - в противном случае мы их в синтаксическом дереве не найдем.
Пример получения исходного кода метода двумя данными способами:
using System; using System.Text; using System.Collections.Generic; using System.IO; using System.Linq; using System.Diagnostics; using System.Reflection; using Microsoft.Diagnostics.Runtime; using Microsoft.Diagnostics.Runtime.Utilities.Pdb; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.Syntax; namespace ConsoleTest1 { class Program { public static string GetSourceFromPdb<T>(Predicate<T> match) { int token = match.Method.MetadataToken; //построим путь к файлу символов string module_path = match.Method.Module.FullyQualifiedName; string pdb_path = Path.Combine( Path.GetDirectoryName(module_path), Path.GetFileNameWithoutExtension(module_path) + ".pdb" ); StringBuilder sb = new StringBuilder(); PdbReader reader = new PdbReader(pdb_path); using (reader) { //найдем метод в символах var func = reader.GetFunctionFromToken((uint)token); foreach (PdbSequencePointCollection coll in func.SequencePoints) { //считываем файл исходников string[] lines = File.ReadAllLines(coll.File.Name, System.Text.Encoding.UTF8); //найдем номера строк в файле, соответствующие началу и концу метода var points_sorted = coll.Lines. Where<PdbSequencePoint>((x)=> x.LineBegin <= lines.Length && x.LineEnd<=lines.Length). OrderBy<PdbSequencePoint, uint>((x) => x.Offset); PdbSequencePoint start = points_sorted.First(); PdbSequencePoint end = points_sorted.Last(); bool reading = false; int index_start; int index_end; //считаем код метода из исходников for(int i=1; i<=lines.Length;i++) { string line = lines[i-1]; index_start = 0; index_end = line.Length; if (!reading) { if (i >= start.LineBegin) { //первая строка reading = true; index_start = start.ColBegin - 1; if (index_start < 0) index_start = 0; } } if (reading) { if (i >= end.LineEnd) { //последняя строка index_end = end.ColEnd - 1; if (index_end > line.Length) index_end = line.Length; sb.AppendLine(line.Substring(index_start, index_end - index_start)); break; } //считывание текущей строки sb.AppendLine(line.Substring(index_start, index_end - index_start)); } } } } return sb.ToString(); } public static AstNode FindNode(AstNode root,string match) { //поиск метода в синтаксическом дереве var children = root.Children; foreach (AstNode x in children) { var s = x.ToString(); if (s == match && x.Parent.NodeType == NodeType.Member) { return x.Parent; } AstNode res = FindNode(x, match); if (res != null) return res; } return null; } public static string GetSourceDecompiled<T>(Predicate<T> match) { string module_path = match.Method.Module.FullyQualifiedName; var settings = new DecompilerSettings(); settings.AnonymousMethods = false; //отключим подстановку кода анонимных методов var decompiler = new CSharpDecompiler( module_path, settings ); //декомпилируем тип, содержащий метод SyntaxTree tree = decompiler.DecompileType( new ICSharpCode.Decompiler.TypeSystem.FullTypeName(match.Method.DeclaringType.FullName) ); //найдем метод в синтаксическом дереве var children = tree.Children.ToList(); AstNode res = null; foreach (var x in children) { res = FindNode(x, match.Method.Name); if (res != null) break; } string s = ""; if (res != null) s = res.ToString(); return s; } static void Main(string[] args) { string source; Console.WriteLine("*** Source from PDB: ***"); source = GetSourceFromPdb<string>( (s) => s == "Test" || s.Length==0); Console.WriteLine(source); Console.WriteLine("*** Decompiled source: ***"); source = GetSourceDecompiled<string>((s) => s == "Test" || s.Length==0); Console.WriteLine(source); Console.ReadKey(); } } }
Результат:
*** Source from PDB: *** s == "Test" || s.Length==0 *** Decompiled source: *** [CompilerGenerated] private static bool <Main>b__6 (string s) { return s == "Test" || s.Length == 0; }
Content is retrieved from StackExchange API.
Auto-generated by ruso-archive tools.