C#中是否有方法来检查字符串是否是有效的标识符

在Java中,在Character类上有一些名为isJavaIdentifierStartisJavaIdentifierPart方法,可用于判断字符串是否是有效的Java标识符,如下所示:

 public boolean isJavaIdentifier(String s) { int n = s.length(); if (n==0) return false; if (!Character.isJavaIdentifierStart(s.charAt(0))) return false; for (int i = 1; i < n; i++) if (!Character.isJavaIdentifierPart(s.charAt(i))) return false; return true; } 

C#有这样的东西吗?

基本上是这样的:

 const string start = @"(\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl})"; const string extend = @"(\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf})"; Regex ident = new Regex(string.Format("{0}({0}|{1})*", start, extend)); s = s.Normalize(); return ident.IsMatch(s); 

是:

 // using System.CodeDom.Compiler; CodeDomProvider provider = CodeDomProvider.CreateProvider("C#"); if (provider.IsValidIdentifier (YOUR_VARIABLE_NAME)) { // Valid } else { // Not valid } 

从这里: 如何确定字符串是否是有效的变量名称?

我会对这里提供的其他解决方案保持警惕。 调用CodeDomProvider.CreateProvider需要查找和解析Machine.Config文件以及app.config文件。 这可能比检查你自己的弦所需的时间慢几倍。

相反,我会主张你做出以下改变之一:

  1. 将提供程序缓存在静态变量中。

    这将导致您只创建一次创建它,但它会减慢类型加载速度。

  2. 通过创建自己的Microsoft.CSharp.CSharpCodeProvider实例直接创建提供程序

    这将跳过配置文件一起解析。

  3. 编写代码来实现检查自己。

    如果这样做,您可以最大程度地控制其实施方式,如果需要,可以帮助您优化性能。 有关C#标识符的完整词法语法,请参阅C#语言规范的 2.2.4节。

由于Roslyn是开源的,代码分析工具就在您的指尖,并且它们是为了性能而编写的。 (现在他们正在预发布)。

但是,我无法谈论装载组件的性能成本。

使用nuget安装工具:

 Install-Package Microsoft.CodeAnalysis -Pre 

问你的问题:

 var isValid = Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier("I'mNotValid"); Console.WriteLine(isValid); // False 

Necromancing在这里。

在.NET Core / DNX中,您可以使用Roslyn-SyntaxFacts来完成

 Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsReservedKeyword( Microsoft.CodeAnalysis.CSharp.SyntaxFacts.GetKeywordKind("protected") ); foreach (ColumnDefinition cl in tableColumns) { sb.Append(@" public "); sb.Append(cl.DOTNET_TYPE); sb.Append(" "); // for keywords //if (!Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier(cl.COLUMN_NAME)) if (Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsReservedKeyword( Microsoft.CodeAnalysis.CSharp.SyntaxFacts.GetKeywordKind(cl.COLUMN_NAME) )) sb.Append("@"); sb.Append(cl.COLUMN_NAME); sb.Append("; // "); sb.AppendLine(cl.SQL_TYPE); } // Next cl 

或者使用Codedom的旧版本 – 查看单声道源代码后:

CodeDomProvider.cs

 public virtual bool IsValidIdentifier (string value) 286 { 287 ICodeGenerator cg = CreateGenerator (); 288 if (cg == null) 289 throw GetNotImplemented (); 290 return cg.IsValidIdentifier (value); 291 } 292 

然后是CSharpCodeProvider.cs

 public override ICodeGenerator CreateGenerator() 91 { 92 #if NET_2_0 93 if (providerOptions != null && providerOptions.Count > 0) 94 return new Mono.CSharp.CSharpCodeGenerator (providerOptions); 95 #endif 96 return new Mono.CSharp.CSharpCodeGenerator(); 97 } 

然后是CSharpCodeGenerator.cs

 protected override bool IsValidIdentifier (string identifier) { if (identifier == null || identifier.Length == 0) return false; if (keywordsTable == null) FillKeywordTable (); if (keywordsTable.Contains (identifier)) return false; if (!is_identifier_start_character (identifier [0])) return false; for (int i = 1; i < identifier.Length; i ++) if (! is_identifier_part_character (identifier [i])) return false; return true; } private static System.Collections.Hashtable keywordsTable; private static string[] keywords = new string[] { "abstract","event","new","struct","as","explicit","null","switch","base","extern", "this","false","operator","throw","break","finally","out","true", "fixed","override","try","case","params","typeof","catch","for", "private","foreach","protected","checked","goto","public", "unchecked","class","if","readonly","unsafe","const","implicit","ref", "continue","in","return","using","virtual","default", "interface","sealed","volatile","delegate","internal","do","is", "sizeof","while","lock","stackalloc","else","static","enum", "namespace", "object","bool","byte","float","uint","char","ulong","ushort", "decimal","int","sbyte","short","double","long","string","void", "partial", "yield", "where" }; static void FillKeywordTable () { lock (keywords) { if (keywordsTable == null) { keywordsTable = new Hashtable (); foreach (string keyword in keywords) { keywordsTable.Add (keyword, keyword); } } } } static bool is_identifier_start_character (char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '@' || Char.IsLetter (c); } static bool is_identifier_part_character (char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9') || Char.IsLetter (c); } 

你得到这个代码:

 public static bool IsValidIdentifier (string identifier) { if (identifier == null || identifier.Length == 0) return false; if (keywordsTable == null) FillKeywordTable(); if (keywordsTable.Contains(identifier)) return false; if (!is_identifier_start_character(identifier[0])) return false; for (int i = 1; i < identifier.Length; i++) if (!is_identifier_part_character(identifier[i])) return false; return true; } internal static bool is_identifier_start_character(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '@' || char.IsLetter(c); } internal static bool is_identifier_part_character(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9') || char.IsLetter(c); } private static System.Collections.Hashtable keywordsTable; private static string[] keywords = new string[] { "abstract","event","new","struct","as","explicit","null","switch","base","extern", "this","false","operator","throw","break","finally","out","true", "fixed","override","try","case","params","typeof","catch","for", "private","foreach","protected","checked","goto","public", "unchecked","class","if","readonly","unsafe","const","implicit","ref", "continue","in","return","using","virtual","default", "interface","sealed","volatile","delegate","internal","do","is", "sizeof","while","lock","stackalloc","else","static","enum", "namespace", "object","bool","byte","float","uint","char","ulong","ushort", "decimal","int","sbyte","short","double","long","string","void", "partial", "yield", "where" }; internal static void FillKeywordTable() { lock (keywords) { if (keywordsTable == null) { keywordsTable = new System.Collections.Hashtable(); foreach (string keyword in keywords) { keywordsTable.Add(keyword, keyword); } } } } 

最近,我编写了一个扩展方法,将字符串validation为有效的C#标识符。

你可以在这里找到一个实现的要点: https : //gist.github.com/FabienDehopre/5245476

它基于Identifier的MSDN文档( http://msdn.microsoft.com/en-us/library/aa664670(v=vs.71).aspx )

 public static bool IsValidIdentifier(this string identifier) { if (String.IsNullOrEmpty(identifier)) return false; // C# keywords: http://msdn.microsoft.com/en-us/library/x53a06bb(v=vs.71).aspx var keywords = new[] { "abstract", "event", "new", "struct", "as", "explicit", "null", "switch", "base", "extern", "object", "this", "bool", "false", "operator", "throw", "breal", "finally", "out", "true", "byte", "fixed", "override", "try", "case", "float", "params", "typeof", "catch", "for", "private", "uint", "char", "foreach", "protected", "ulong", "checked", "goto", "public", "unchekeced", "class", "if", "readonly", "unsafe", "const", "implicit", "ref", "ushort", "continue", "in", "return", "using", "decimal", "int", "sbyte", "virtual", "default", "interface", "sealed", "volatile", "delegate", "internal", "short", "void", "do", "is", "sizeof", "while", "double", "lock", "stackalloc", "else", "long", "static", "enum", "namespace", "string" }; // definition of a valid C# identifier: http://msdn.microsoft.com/en-us/library/aa664670(v=vs.71).aspx const string formattingCharacter = @"\p{Cf}"; const string connectingCharacter = @"\p{Pc}"; const string decimalDigitCharacter = @"\p{Nd}"; const string combiningCharacter = @"\p{Mn}|\p{Mc}"; const string letterCharacter = @"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}"; const string identifierPartCharacter = letterCharacter + "|" + decimalDigitCharacter + "|" + connectingCharacter + "|" + combiningCharacter + "|" + formattingCharacter; const string identifierPartCharacters = "(" + identifierPartCharacter + ")+"; const string identifierStartCharacter = "(" + letterCharacter + "|_)"; const string identifierOrKeyword = identifierStartCharacter + "(" + identifierPartCharacters + ")*"; var validIdentifierRegex = new Regex("^" + identifierOrKeyword + "$", RegexOptions.Compiled); var normalizedIdentifier = identifier.Normalize(); // 1. check that the identifier match the validIdentifer regex and it's not a C# keyword if (validIdentifierRegex.IsMatch(normalizedIdentifier) && !keywords.Contains(normalizedIdentifier)) { return true; } // 2. check if the identifier starts with @ if (normalizedIdentifier.StartsWith("@") && validIdentifierRegex.IsMatch(normalizedIdentifier.Substring(1))) { return true; } // 3. it's not a valid identifier return false; } 

现在发布的Roslyn项目提供了Microsoft.CodeAnalysis.CSharp.SyntaxFacts ,其中包含SyntaxFacts.IsIdentifierStartCharacter(char)SyntaxFacts.IsIdentifierPartCharacter(char)方法,就像Java一样。

在这里它正在使用中,我用一个简单的函数将名词短语(例如“开始日期”)转换成C#标识符(例如“StartDate”)。 注意我正在使用Humanizer进行驼峰转换,而Roslyn则检查字符是否有效。

  public static string Identifier(string name) { Check.IsNotNullOrWhitespace(name, nameof(name)); // trim off leading and trailing whitespace name = name.Trim(); // should deal with spaces => camel casing; name = name.Dehumanize(); var sb = new StringBuilder(); if (!SyntaxFacts.IsIdentifierStartCharacter(name[0])) { // the first characters sb.Append("_"); } foreach(var ch in name) { if (SyntaxFacts.IsIdentifierPartCharacter(ch)) { sb.Append(ch); } } var result = sb.ToString(); if (SyntaxFacts.GetKeywordKind(result) != SyntaxKind.None) { result = @"@" + result; } return result; } 

检验。

  [TestCase("Start Date", "StartDate")] [TestCase("Bad*chars", "BadChars")] [TestCase(" leading ws", "LeadingWs")] [TestCase("trailing ws ", "TrailingWs")] [TestCase("class", "Class")] [TestCase("int", "Int")] [Test] public void CSharp_GeneratesDecentIdentifiers(string input, string expected) { Assert.AreEqual(expected, CSharp.Identifier(input)); } 

这可以使用reflection来完成 – 请参阅如何确定字符串是否是有效的变量名称?