问题描述
这个问题不仅是问硬编码字符串,但幻数等为好。
有没有办法找到所有的硬编码值即字符串,幻数,什么不能在C#项目/在VS解决方案?
Is there a way to find all the hard coded values i.e. string , magic numbers and what not in C# project/solution in VS?
是什么促使这个问题是我在看一个项目,我刚刚发现174倍的字符串值hardcodely重复!
What prompted this question is a project that I am looking at, I just found 174 times a string value was hardcodely repeated!
推荐答案
你可以做的是节目,在镇(不那么)新酷小孩。它可以让你很容易地解析C#(或VB.NET)项目。然后,你可以参观检测节点,并检查你真的要检查什么。检测魔法文字的机器并不总是因为它似乎为人类一样容易。例如,为1真是一个神奇的数字?我个人认为不是,但2更疑...
What you could do is program Roslyn, the (not so) new cool kid in town. It allows you to parse C# (or VB.NET) projects quite easily. Then you can visit the detected nodes and check what you really want to check. Detecting magic literals for a machine is not always as easy as it seems for a human. For example, is 1 really a magic number? I personally consider it's not, but 2 is more suspect...
总之,这里是一个小样本,做我认为这项工作的很大一部分,但它可能/还有待完善,也许定制您的具体业务需求或规则(这是很有趣的)。
Anyway, here is a small sample that does a good part of the job I believe, but it could/should be improved, maybe to tailor your exact business needs or rules (which is very interesting).
请注意罗斯林也可以直接在Visual Studio中的上下文中使用,所以你可以把这个样品到什么叫做诊断(扩展到Visual Studio),可以帮助你在IDE中直接住。这有样本:
Note Roslyn can also be used directly in the context of Visual Studio, so you could turn this sample into what's called a diagnostic (an extension to Visual Studio) that can help you directly live from within the IDE. There are samples for this: Samples and Walkthroughs
class Program
{
static void Main(string[] args)
{
var text = @"
public class MyClass
{
public void MyMethod()
{
const int i = 0; // this is ok
decimal d = 11; // this is not ok
string s = ""magic"";
if (i == 29) // another magic
{
}
else if (s != ""again another magic"")
{
}
}
}";
ScanHardcodedFromText("test.cs", text, (n, s) =>
{
Console.WriteLine(" " + n.SyntaxTree.GetLineSpan(n.FullSpan) + ": " + s);
}).Wait();
}
public static async Task ScanHardcodedFromText(string documentName, string text, Action<SyntaxNodeOrToken, string> scannedFunction)
{
if (text == null)
throw new ArgumentNullException("text");
AdhocWorkspace ws = new AdhocWorkspace();
var project = ws.AddProject(documentName + "Project", LanguageNames.CSharp);
ws.AddDocument(project.Id, documentName, SourceText.From(text));
await ScanHardcoded(ws, scannedFunction);
}
public static async Task ScanHardcodedFromSolution(string solutionFilePath, Action<SyntaxNodeOrToken, string> scannedFunction)
{
if (solutionFilePath == null)
throw new ArgumentNullException("solutionFilePath");
var ws = MSBuildWorkspace.Create();
await ws.OpenSolutionAsync(solutionFilePath);
await ScanHardcoded(ws, scannedFunction);
}
public static async Task ScanHardcodedFromProject(string solutionFilePath, Action<SyntaxNodeOrToken, string> scannedFunction)
{
if (solutionFilePath == null)
throw new ArgumentNullException("solutionFilePath");
var ws = MSBuildWorkspace.Create();
await ws.OpenProjectAsync(solutionFilePath);
await ScanHardcoded(ws, scannedFunction);
}
public static async Task ScanHardcoded(Workspace workspace, Action<SyntaxNodeOrToken, string> scannedFunction)
{
if (workspace == null)
throw new ArgumentNullException("workspace");
if (scannedFunction == null)
throw new ArgumentNullException("scannedFunction");
foreach (var project in workspace.CurrentSolution.Projects)
{
foreach (var document in project.Documents)
{
var tree = await document.GetSyntaxTreeAsync();
var root = await tree.GetRootAsync();
foreach (var n in root.DescendantNodesAndTokens())
{
if (!CanBeMagic(n.Kind()))
continue;
if (IsWellKnownConstant(n))
continue;
string suggestion;
if (IsMagic(n, out suggestion))
{
scannedFunction(n, suggestion);
}
}
}
}
}
public static bool IsMagic(SyntaxNodeOrToken kind, out string suggestion)
{
var vdec = kind.Parent.Ancestors().OfType<VariableDeclarationSyntax>().FirstOrDefault();
if (vdec != null)
{
var dec = vdec.Parent as MemberDeclarationSyntax;
if (dec != null)
{
if (!HasConstOrEquivalent(dec))
{
suggestion = "member declaration could be const: " + dec.ToFullString();
return true;
}
}
else
{
var ldec = vdec.Parent as LocalDeclarationStatementSyntax;
if (ldec != null)
{
if (!HasConstOrEquivalent(ldec))
{
suggestion = "local declaration contains at least one non const value: " + ldec.ToFullString();
return true;
}
}
}
}
else
{
var expr = kind.Parent.Ancestors().OfType<ExpressionSyntax>().FirstOrDefault();
if (expr != null)
{
suggestion = "expression uses a non const value: " + expr.ToFullString();
return true;
}
}
// TODO: add other cases?
suggestion = null;
return false;
}
private static bool IsWellKnownConstant(SyntaxNodeOrToken node)
{
if (!node.IsToken)
return false;
string text = node.AsToken().Text;
if (text == null)
return false;
// note: this is naïve. we also should add 0d, 0f, 0m, etc.
if (text == "1" || text == "-1" || text == "0")
return true;
// ok for '\0' or '\r', etc.
if (text.Length == 4 && text.StartsWith("'\\") && text.EndsWith("'"))
return true;
if (text == "' '")
return true;
// TODO add more of these? or make it configurable...
return false;
}
private static bool HasConstOrEquivalent(SyntaxNode node)
{
bool hasStatic = false;
bool hasReadOnly = false;
foreach (var tok in node.ChildTokens())
{
switch (tok.Kind())
{
case SyntaxKind.ReadOnlyKeyword:
hasReadOnly = true;
if (hasStatic)
return true;
break;
case SyntaxKind.StaticKeyword:
hasStatic = true;
if (hasReadOnly)
return true;
break;
case SyntaxKind.ConstKeyword:
return true;
}
}
return false;
}
private static bool CanBeMagic(SyntaxKind kind)
{
return kind == SyntaxKind.CharacterLiteralToken ||
kind == SyntaxKind.NumericLiteralToken ||
kind == SyntaxKind.StringLiteralToken;
}
}
如果你运行这个小程序(我也提供辅助方法把它用在解决方案或项目),它会输出这样的:
If you run this little program (I've also provided helper methods to use it on solution or projects), it will output this:
test.cs: (6,20)-(6,22): local declaration contains at least one non const value: decimal d = 11; // this is not ok
test.cs: (7,19)-(7,26): local declaration contains at least one non const value: string s = "magic";
test.cs: (8,17)-(8,19): expression uses a non const value: i == 29
test.cs: (11,22)-(11,43): expression uses a non const value: s != "again another magic"
这篇关于如何找到一个C#项目(解决方案)中的所有硬编码值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!