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

发布于 2024-08-14 17:01:13 字数 475 浏览 10 评论 0 原文

在 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# 的吗?

In Java, there are methods called isJavaIdentifierStart and isJavaIdentifierPart on the Character class that may be used to tell if a string is a valid Java identifier, like so:

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;
}

Is there something like this for C#?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

夏日浅笑〃 2024-08-21 17:01:13

是:

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

从这里:如何确定是否string 是有效的变量名吗?

Yes:

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

From here: How to determine if a string is a valid variable name?

青衫负雪 2024-08-21 17:01:13

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

相反,我建议您进行以下更改之一:

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

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

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

    这将跳过所有配置文件解析。

  3. 编写代码来实现您的自我检查。

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

I would be wary of the other solutions offered here. Calling CodeDomProvider.CreateProvider requires finding and parsing the Machine.Config file, as well as your app.config file. That's likely to be several times slower than the time required to just check the string your self.

Instead I would advocate you make one of the following changes:

  1. Cache the provider in a static variable.

    This will cause you to take the hit of creating it only once, but it will slow down type loading.

  2. Create the provider directly, by creating a Microsoft.CSharp.CSharpCodeProvider instance your self

    This will skip the config file parsing all together.

  3. Write the code to implement the check your self.

    If you do this, you get the greatest control over how it's implemented, which can help you optimize performance if you need to. See section 2.2.4 of the C# language spec for the complete lexical grammar for C# identifiers.

走过海棠暮 2024-08-21 17:01:13

由于 Roslyn 是开源的,代码分析工具触手可及,而且它们是为性能而编写的。 (目前它们处于预发布状态)。

但是,我无法谈论加载程序集的性能成本。

使用 nuget 安装工具:

Install-Package Microsoft.CodeAnalysis -Pre

提出您的问题:

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

With Roslyn being open source, code analysis tools are right at your fingertips, and they're written for performance. (Right now they're in pre-release).

However, I can't speak to the performance cost of loading the assembly.

Install the tools using nuget:

Install-Package Microsoft.CodeAnalysis -Pre

Ask your question:

var isValid = Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier("I'mNotValid");
Console.WriteLine(isValid);     // False
待"谢繁草 2024-08-21 17:01:13

基本上是这样的:

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);

Basically something like:

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);
画▽骨i 2024-08-21 17:01:13

这里有亡灵术。

在 .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);
            }
        }
    }
}

Necromancing here.

In .NET Core/DNX, you can do it with 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 

Or in the old variant with Codedom - After a look in the mono sourcecode:

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  

Then 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      } 

Then 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);
}

You get this code:

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);
            }
        }
    }
}
爱的十字路口 2024-08-21 17:01:13

最近,我编写了一个扩展方法,用于验证字符串是否为有效的 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;
}

Recently, I wrote an extension method that validates a string as a valid C# identifier.

You can find a gist with the implementation here: https://gist.github.com/FabienDehopre/5245476

It's based on the MSDN documentation of Identifier (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;
}
很糊涂小朋友 2024-08-21 17:01:13

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

这里正在使用它,在一个简单的函数中,我使用它来将名词短语(例如“Start Date”)转换为 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));
    }

The now-released Roslyn project provides Microsoft.CodeAnalysis.CSharp.SyntaxFacts, with SyntaxFacts.IsIdentifierStartCharacter(char) and SyntaxFacts.IsIdentifierPartCharacter(char) methods just like Java.

Here it is in use, in a simple function I use to turn noun phrases (eg "Start Date") into C# identifiers (eg "StartDate"). N.B I'm using Humanizer to do the camel-case conversion, and Roslyn to check whether a character is valid.

    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;
    }

Tests;

    [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));
    }
痴骨ら 2024-08-21 17:01:13

这可以使用反射来完成 - 请参阅如何判断一个字符串是否是一个有效的变量名?

This can be done using reflection - see How to determine if a string is a valid variable name?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文