-
-
Save xoposhiy/bc2a22984817113e0f8d to your computer and use it in GitHub Desktop.
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using Microsoft.CodeAnalysis; | |
using Microsoft.CodeAnalysis.CSharp; | |
using Microsoft.CodeAnalysis.CSharp.Syntax; | |
namespace solution | |
{ | |
public class Program | |
{ | |
private static void Main(string[] args) | |
{ | |
/* | |
Решением тестового задания считается исходный код консольного приложения на C#, | |
принимающее в качестве единственного аргумента командной строки путь до директории | |
с исходными кодами на языке C#, | |
и выводящее в файл long.txt список самых длинных функций, | |
а в файл nesting.txt — список функций с самым большим уровнем вложенности операторов. | |
*/ | |
var functions = | |
Directory.EnumerateFiles(args[0], "*.cs", SearchOption.AllDirectories) | |
.SelectMany(GetFunctionInfos) | |
.ToList(); | |
OutputMetricRatings(functions, "long.txt", m => m.Length); | |
OutputMetricRatings(functions, "nesting.txt", m => m.MaxNestingLevel); | |
} | |
private static IEnumerable<FunctionInfo> GetFunctionInfos(string sourceFile) | |
{ | |
string source = File.ReadAllText(sourceFile); | |
var syntaxTree = CSharpSyntaxTree.ParseText(source); | |
return syntaxTree.GetRoot().DescendantNodes() | |
.Where(IsFunction) | |
.Select(node => | |
new FunctionInfo | |
{ | |
Filename = sourceFile, | |
StartLineNumber = GetLineNumber(syntaxTree, node), | |
Length = GetLength(node), | |
MaxNestingLevel = GetNesting(node) | |
}); | |
} | |
private static bool IsFunction(SyntaxNode member) | |
{ | |
/* | |
Функцией в рамках этой задачи мы называем любой член класса или структуры, | |
который содержит внутри себя операторы (statements), | |
за исключением вложенных типов и полей. | |
*/ | |
return member is MemberDeclarationSyntax | |
&& !(member is FieldDeclarationSyntax) | |
&& !(member is NamespaceDeclarationSyntax) | |
&& !(member is StructDeclarationSyntax) | |
&& !(member is ClassDeclarationSyntax) | |
&& !(member is EnumDeclarationSyntax) | |
&& member.DescendantNodes().Any(c => c is StatementSyntax); | |
} | |
private static int GetLineNumber(SyntaxTree syntaxTree, SyntaxNode n) | |
{ | |
return syntaxTree.GetLineSpan(n.Span).StartLinePosition.Line + 1; | |
} | |
private static int GetLength(SyntaxNode member) | |
{ | |
/* | |
Длина функции — это количество операторов (statements), которые содержит метод, | |
не считая блочных операторов (block statement) | |
*/ | |
return | |
member.DescendantNodes() | |
.Count(n => n is StatementSyntax && !(n is BlockSyntax)); | |
} | |
private static int GetNesting(SyntaxNode member) | |
{ | |
/* | |
Уровень вложенности функции — максимальный уровень вложенности среди всех операторов, | |
содержащихся внутри этой функции. | |
*/ | |
return member.DescendantNodes().Where(n => n is StatementSyntax) | |
.Max(s => GetParentsAndSelf(s).Count(IsNestingNode)); | |
} | |
private static bool IsNestingNode(SyntaxNode node) | |
{ | |
/* | |
Если рассмотреть в синтаксическом дереве функции путь от отдельного оператора до корня дерева, | |
то каждая из следующих синтаксических конструкций добавляет единицу | |
к уровню вложенности этого оператора: | |
if, switch, for, foreach, do, while, checked, unchecked, fixed, using, lock, unsafe, | |
try, catch, finally, определение анонимного метода, определение лямбда выражения. | |
*/ | |
return node is IfStatementSyntax | |
|| node is SwitchStatementSyntax | |
|| node is ForStatementSyntax | |
|| node is ForEachStatementSyntax | |
|| node is DoStatementSyntax | |
|| node is WhileStatementSyntax | |
|| node is CheckedStatementSyntax //unchecked — это тоже CheckedStatementSyntax | |
|| node is FixedStatementSyntax | |
|| node is UsingStatementSyntax | |
|| node is LockStatementSyntax | |
|| node is UnsafeStatementSyntax | |
|| node is TryStatementSyntax // catch и finally — это составные части Try | |
|| node is AnonymousMethodExpressionSyntax | |
|| node is SimpleLambdaExpressionSyntax | |
|| node is ParenthesizedLambdaExpressionSyntax; | |
} | |
private static IEnumerable<SyntaxNode> GetParentsAndSelf(SyntaxNode statement) | |
{ | |
while (statement != null) | |
{ | |
yield return statement; | |
statement = statement.Parent; | |
} | |
} | |
private static void OutputMetricRatings( | |
IEnumerable<FunctionInfo> functions, string filename, Func<FunctionInfo, int> getMetric) | |
{ | |
/* | |
Top-100 нужно брать у списка, отсортированного сначала | |
по измеряемому параметру (длине или уровню вложенности) по убыванию, | |
при равных значениях — по имени файла по возрастанию, | |
при прочих равных — по номеру строки по возрастанию. | |
*/ | |
File.WriteAllLines( | |
filename, | |
functions | |
.OrderByDescending(getMetric) | |
.ThenBy(function => function.Filename) | |
.ThenBy(function => function.StartLineNumber) | |
.Select(function => FormatMetricValue(function, getMetric(function))) | |
.Take(100)); | |
} | |
private static string FormatMetricValue(FunctionInfo function, int metricValue) | |
{ | |
// [value][TAB][filename]:[lineNumber] | |
return string.Format("{0}\t{1}:{2}", | |
metricValue, | |
Path.GetFileName(function.Filename), | |
function.StartLineNumber); | |
} | |
} | |
public class FunctionInfo | |
{ | |
public string Filename { get; set; } | |
public int StartLineNumber { get; set; } | |
public int Length { get; set; } | |
public int MaxNestingLevel { get; set; } | |
} | |
} |
Опередил на счет лямбды, в коде есть несоответствие с условием задачи: SimpleLambdaExpressionSyntax
не нужно считать, потому что это простое лямбда выражение, которое в условии сказано не учитывать.
Ильмир, по 1ому:
if(){}else if(){}
вложенность 2, потому что кострукция if внутри кострукции if-else,
а блок try{}catch{}finally{}
- 1, потому что это единая конструкция.
по 2 и 3ему:
Если тело лямбды простое (т.е. expression ), то это SimpleLambdaExpressionSyntax
, а если с фигурной скобкой (т.е. statement) - ParenthesizedLambdaExpressionSyntax
Вы не правы проверьте этот код стандартным инструментом Roslyn Syntax Visualizers входящий в состав Roslyn.
Func<bool, bool> not = b => !b;
Func<bool, bool> not1 = b => {return !b; };
в дереве Roslyn Syntax Visualizers являются классами SimpleLambdaExpressionSyntax что со скобками что без.
а вот (int x, string s) => s.Length > x; это является уже ParenthesizedLambdaExpressionSyntax(это выражения лямбда)
так же если посмотреть на синтаксическое дерево которое получается в Roslyn Syntax Visualizers правильней будет учитывать catch и finally. Потому что в этом дереве так же располагается второй if, в структуре if-else-if как и finally-catch в структуре try-finally-catch и не важно что finally и catch не могут существовать без try, ведь в ТЗ написано про catch и finally, и написано мне кажется об этом не просто так, а с каким то смыслом.
А если просто находить всех предков стейтмента, то даже не придется задумываться о таких вещах.
- В условии было написано лямбда выражения. Кажется, должно быть очевидно, что подразумеваются оба вида и такие: a => a+1 и такие (int a) => a+1. Если это казалось не очевидно, была возможность задать вопрос.
- А это не нужно проверять фигурные скобки. Мы же считаем максимум вложенности по всем statement-ам. Этого достаточно. Более того, если вы будете проверять наличие скобочек, то может быть ошибка на таком примере: a => (b => { f(); }) тут есть statement-ы, но чуть глубже.
Про первый пункт надо подумать и перепроверить. Действительно похоже на косяк в условии задачи. Правда тесты были устроены так, что сильно это на результаты повлиять не должно было.
Фаридонов Ильмир:
Если рассматривать синтаксическое дерево получившее в результате команды CSharpSyntaxTree.ParseText(source);
то для оператора if-elseif(1) будет уровень вложенности 2, так как второй оператор if будет вложен в else, раз так, тогда справедливо это и для try-catch-finaly(2), потому что в синтаксическом дереве сatch и finaly будет вложен try, как второй if в операторе if-elseif
Код(1)
if(...){ // за if +1
}elseif(...){ // еще if +1
} // итого 2
Код(2)
try{ +1
}catch{}+ 1 (В ТЗ есть Catch)
}finally{} (В ТЗ есть finaly, но уровень все равно будет 2) итого 2
Вы же просто учитываете try без catch и finally, и по почему то обрабатываете второй if в конструкции if-elseif
3)ИЗ ТЗ: Заметьте, что вложенность функции f2 — единица, а не двойка. Так получается, потому что тело простого лямбда выражения (тело которого не заключено в фигурные скобки) согласно спецификации — это не statement, а expression, а значит, согласно описанным выше правилам, «b =>!b» не влияет на вычисление уровня вложенности.
А соответственно где вы проверяете что тело Лямбда выражения заключено/не заключено в фигурные скобки?