来自Int的C#Char用作String – VB Chr()的真正等价物

我正在将VBA宏转换为C#。 在VBA中, chr(7)可以简单地连接到一个string ,就像chr()会产生一个string 。 为什么不能在C#中完成?

我试图找到我的问题的明确答案。 我已经在SO和其他几个网站上阅读了很多关于此的post和相关问题。 例如,这是一个关键答案(许多其他人被标记为dulpicates并重定向到这个): 在C#中VB的Asc()和Chr()函数的等价物是什么?

不幸的是,答案并不清楚,很多时候他们说这是正确的用法:

 string mystring=(char)7; 

然而它给了我一个编译器错误,因为它不作为字符串进行求值。

我不得不用它来使它工作:

 string mystring=((char)7).ToString(); 

这将等同于VB Chr()函数,实际上VB中的Chr()计算为字符串。

我的问题是:我是否总是需要将char显式转换为string或者在某些情况下它会隐式转换?

更新:

Per @ Dirk的答案,这也有效:

 string mystring = "" + (char)7; 

这并没有减少我的神秘感。 如果连接有效,为什么没有隐式转换?

我想对VB Chr()与C#中的等价物之间的区别进行全面解释。 我会很感激任何我可以阅读的参考资料,甚至可以做的例子。 提前致谢。

你正在用这个问题打开潘多拉盒子。 Chr()是VB.NET中的遗留函数,任何现代代码都应该使用ChrW()。 区别在于应该解释字符值的方式,ChrW()假定字符代码是Unicode(W =宽)。 Chr()将时钟回滚到上个世纪,这是一个没有Unicode的石器时代,其中字符是ASCII字符集(0..127)或“扩展”字符(128..255)。 扩展字符属于代码页的位置 。 许多不同的代码页都是常用的。 这是一个非常重大的灾难,程序无法正确解释由位于不同国家/地区的另一台计算机生成的文本。 甚至在同一个国家,日本也有多个共同使用的代码页,但没有一个占主导地位。 制作mojibake 。

我假设你的意思是ChrW(),没有人喜欢mojibake。 也不是C#。 使用Char.ToString()很好,另一种方法是使用带有char的字符串构造函数 :

  string mystring = new string((char)7, 1); 

或者您可能更喜欢的更一般的forms:

  public static string ChrW(int code) { return new string((char)code, 1); } 

不是唯一的方法,使用文字是可能的,并且可能是你喜欢的辅助方法。 而且C#不需要像Chr()这样的辅助函数的基本原因。 ASCII控制代码7是铃声字符,当你把它写到控制台时它会让你感到烦恼,你可以使用转义:

  string mystring = "\a"; 

不完全令人难忘,这来自Unix。 其他的是退格时的“\ b”,选项卡的“\ t”,回车符的“\ r”和换行符的“\ n”。 擦除控制台窗口中最后一个类型字符的经典技巧是Console.Write("\b \b"); 。 应注意Environment.NewLine属性。 至于你应该用控制字符来推动它。

最后但并非最不重要的是,\ U和\ u说明符可以让你编码任何字符:

  string mystring = "\u0007"; 

从示例中不明显但是\ u值必须是hex的。 当您使用上部Unicode位平面中的代码点时,需要使用U.

如果由于遗留原因绝对必须使用Chr方法,最好的方法是将其用作普通方法 。

如果你不想导入VisualBasic,或者想看看它是如何工作的,Reflector提供了一段很好的代码:

 public static char Chr(int CharCode) { char ch; if ((CharCode < -32768) || (CharCode > 0xffff)) { throw new ArgumentException(Utils.GetResourceString("Argument_RangeTwoBytes1", new string[] { "CharCode" })); } if ((CharCode >= 0) && (CharCode <= 0x7f)) { return Convert.ToChar(CharCode); } try { int num; Encoding encoding = Encoding.GetEncoding(Utils.GetLocaleCodePage()); if (encoding.IsSingleByte && ((CharCode < 0) || (CharCode > 0xff))) { throw ExceptionUtils.VbMakeException(5); } char[] chars = new char[2]; byte[] bytes = new byte[2]; Decoder decoder = encoding.GetDecoder(); if ((CharCode >= 0) && (CharCode <= 0xff)) { bytes[0] = (byte) (CharCode & 0xff); num = decoder.GetChars(bytes, 0, 1, chars, 0); } else { bytes[0] = (byte) ((CharCode & 0xff00) >> 8); bytes[1] = (byte) (CharCode & 0xff); num = decoder.GetChars(bytes, 0, 2, chars, 0); } ch = chars[0]; } catch (Exception exception) { throw exception; } return ch; } 

对于ASCII字符,它只调用Convert.ToChar ,它相当于(char)CharCode 。 第一个有趣的事情是调用Utils.GetLocaleCodePage

 internal static int GetLocaleCodePage() { return Thread.CurrentThread.CurrentCulture.TextInfo.ANSICodePage; } 

虽然人们可能期望它与Encoding.Default相同,但它会创建与当前线程的文化相关联的编码,而不是系统。 其余的只是将代码填充到一个数组中并使用编码对其进行解码。

这种方法有一个主要的警告,就像处理编码时一样 – 它在很大程度上取决于当前的语言环境,而改变当前线程的文化会破坏ASCII以外代码的所有转换。 但是,如果就是你想要做的,这里是一个粗略而简短的等价物:

 public static char Chr(int code) { var encoding = Encoding.GetEncoding(Thread.CurrentThread.CurrentCulture.TextInfo.ANSICodePage); return encoding.GetChars(BitConverter.GetBytes((ushort)code))[0]; } 

这缺少对原始方法的一些检查,尤其是单字节和范围检查。

然后在VB.NET中有一个更简单,更好的方法 – 用于Unicode的ChrW :

 public static char ChrW(int CharCode) { if ((CharCode < -32768) || (CharCode > 0xffff)) { throw new ArgumentException(Utils.GetResourceString("Argument_RangeTwoBytes1", new string[] { "CharCode" })); } return Convert.ToChar((int) (CharCode & 0xffff)); } 

这又回到了ToChar :

 public static char ToChar(int value) { if ((value < 0) || (value > 0xffff)) { throw new OverflowException(Environment.GetResourceString("Overflow_Char")); } return (char) value; } 

正如你所看到的,ChrW与普通的旧char转换相同…… 除了负值! 你知道,虽然字符代码必须符合两个字节,但它可能来自有符号或无符号短,因此该方法确保它对于两种类型的原点都是正确的数字。 如果你想考虑到这一点,只需要CharCode & 0xffff

正如您所看到的, Chr只是Encoding.GetChars ,其中编码是当前线程的编码,而ChrW只是(char)CharCode ,除了两个函数也处理负值。 没有其他区别。


至于问题的原始部分,你不能从char转换为string因为…没有可能的转换。 它们不会相互inheritance,因此您无法转换它们,它们也没有任何用户定义的转换运算符,并且string不是原始值类型,因此也没有内置转换。 VB.NET可能允许你这样做,但总而言之,由于它的古老版本,它允许许多更糟糕的事情。

TL; DR是(char)等同于Chr ? 仅适用于ASCII字符代码(0到127),否则为no 。 如果当前编码和代码编码不同, Chr停止工作,这在使用非ASCII字符时很重要。

只是为了简化语法。 以下AChar类处理转换。

 string A = (AChar)65; Console.WriteLine(A); // output is "A" 

以下类表示一个字符,并定义ASCII代码页的转换:

 struct AChar { public static implicit operator AChar(char value) => new AChar { Value = value }; public static explicit operator AChar(string value) { if (string.IsNullOrEmpty(value)) return '\x0000'; if (value.Length > 1) throw new InvalidCastException("String contains more than 1 character."); return value[0]; } public static explicit operator AChar(long value) { if(value < 0 || value > 0xFF) throw new InvalidCastException("Char code is out of ASCII range."); return (AChar)Encoding.ASCII.GetString(new[] { (byte)value }); } public static implicit operator AChar(byte value) => (AChar)(long)value; public static explicit operator AChar(int value) => (AChar)(long)value; public static implicit operator char(AChar aChar) => aChar.Value; public static implicit operator string(AChar aChar) => aChar.Value.ToString(); public static bool operator==(AChar left, AChar right) => left.Value == right.Value; public static bool operator!=(AChar left, AChar right) => left.Value != right.Value; public static bool operator >(AChar left, AChar right) => left.Value > right.Value; public static bool operator >=(AChar left, AChar right) => left.Value >= right.Value; public static bool operator <(AChar left, AChar right) => left.Value < right.Value; public static bool operator <=(AChar left, AChar right) => left.Value <= right.Value; public override string ToString() => this; public override int GetHashCode() => Value.GetHashCode(); public override bool Equals(object obj) => obj is AChar && ((AChar)obj).Value == Value; char Value { get; set; } } 

首先将字符代码转换为AChar,它与C#的stringstring兼容。

其他答案非常完整。 还有这个C#技巧,您可以使用它来满足角色的心情:

 string mystring = "" + (char)7; 

这通常适用于不能直接分配给字符串的更多类型。 它可能会certificate你不那么难看,并让你在同一行上做更多的连接。