使用 P/Invoke 在 C#/.NET 中以几乎一行代码反转字符串






2.67/5 (8投票s)
如何使用内置的 Windows 库函数(在任何 Windows 系统上都可用)在 C#/.NET 中以几乎一行代码反转字符串。
介绍
这篇短文演示了如何使用 Microsoft Visual C 运行时库中几乎适用于 Windows 95 到最新的 Windows 8 系统的导出函数,在 C# 中用一行代码反转字符串。
背景
有时,出于特定原因,我们需要在 .NET 中反转字符串。通常,开发人员会编写自己的函数和类,其中包含某种类/函数对来完成此任务,除非没有现成的代码或外部库/程序集可用。但是,实现本身的代码质量、速度和大小可能会因开发人员的技能和编译器生成的代码而有很大差异。请记住,除了广泛使用的 Microsoft 编译器之外,还有许多其他编译器可以生成 IL 代码,并且它们在优化和其他影响应用程序性能的变量方面可能有所不同。通过使用 Microsoft Visual C 运行时库函数 _strrev()
和/或 _wcsrev()
,我们可以用一行代码快速反转字符串。使用 .NET Framework 的 Interop 技术,可以轻松地将这些函数导入 .NET 项目并使用它们。CodeProject 上有很多关于 Interop 主题的精彩文章,因此无需详细解释 .NET Interop 是什么。使用这两个 Microsoft 提供的函数,与第三方外部库相比,具有一些主要优势:
- Microsoft Visual C 运行时库从 Windows 95 到最新的 Windows 8,包括 32 位和 64 位版本,在任何 Windows 系统上都毫无例外地可用。该库本身实际上是 Windows 系统的一个组成部分,许多 Windows 应用程序本身就依赖于它的存在才能正常工作。
- 这些函数经过充分文档化且非常稳定,因此可以安全地用于任何项目,没有任何例外。除了存在不同版本并可能安装在系统上这一事实外,在任何库版本中,输入和输出数据始终是相同的。
- 该库代码由 Microsoft 维护,任何错误修复都会在新版本或更新版本的 Microsoft Visual C 运行时库中提供给所有 Windows 系统。
- 代码运行速度非常快。这里的“快”是指实现代码,即库内部的代码,是用纯 C 编写的,唯一需要在库和托管应用程序之间进行封送处理的是字符串(实际上是字符串指针)。这使得即使是大型字符串的反转操作也非常快速。
使用代码
使用代码非常简单,C# 签名可以轻松导入到任何 .NET 语言(例如 VB.NET)中,只需将它们翻译成其语言对应项即可。项目文件包含一个简单的 C# Visual Studio 2008 文件格式的 Forms 示例项目,其中包含一个简单的应用程序,并带有注释代码,说明如何使用这两个函数。现在让我们看看代码。
我们首先要做的是将 System.Runtime.InteropServices
命名空间导入到我们的项目中,以便能够使用 Interop 机制并在我们的项目中调用这两个函数。
using System.Runtime.InteropServices;
现在我们可以在项目中的一个类中声明我们的导入函数签名。它们必须如下所示:
/* Original documentation for _strrev, _wcsrev, _mbsrev, _mbsrev_l
* on the Micdrosoft MSDN Libary pages:
* http://msdn.microsoft.com/en-us/library/9hby7w40%28v=vs.80%29.aspx
*/
// UNICODE variant
[DllImport("msvcrt.dll",
CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string _wcsrev(
[MarshalAs(UnmanagedType.LPWStr)]
[In] string str);
// ANSI variant
[DllImport("msvcrt.dll",
CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string _strrev(
[MarshalAs(UnmanagedType.LPStr)]
[In] string str);
就这样!现在它们可以像任何 C# 函数一样在你的代码中使用:下面是一个简短的示例,展示了如何使用它们。
public partial class Form1 : Form
{
/* Original documentation for _strrev, _wcsrev, _mbsrev, _mbsrev_l
* on the Micdrosoft MSDN Libary pages:
* http://msdn.microsoft.com/en-us/library/9hby7w40%28v=vs.80%29.aspx
*/
// UNICODE variant
[DllImport("msvcrt.dll",
CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string _wcsrev(
[MarshalAs(UnmanagedType.LPWStr)]
[In] string str);
// ANSI variant
[DllImport("msvcrt.dll",
CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string _strrev(
[MarshalAs(UnmanagedType.LPStr)]
[In] string str);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
private void button2_Click(object sender, EventArgs e)
{
// make sure you pass in a valid string
// passing a null will lead to a System.AccessViolationException exception
if (string.IsNullOrEmpty(this.textBox1.Text.Trim()) == true)
{
MessageBox.Show("The original string cant be empty. Please set the original text first!",
this.Text,
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
return;
}
try
{
//we are using the UNICODE variant, since all strings in .NET are UNICODE by default
this.textBox2.Text = _wcsrev(textBox1.Text);
}
catch (Exception err)
{
MessageBox.Show("Error reversing string: " + err.Message,
this.Text,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
}
重要提示
请确保将有效的字符串作为函数参数传递,而不是 null
。传递 ""
或 string.Empty
(运行时它们是相同的)是可以的,但传递 null
作为参数肯定会导致 System.AccessViolationException
类型的异常,如果你不在 try/catch
块中处理该异常,则会崩溃你的应用程序。
历史
- 13/01/03 - 初始发布
- 13/01/04 - 更改了主题文本,使其更符合方法技术
外部链接
_strrev
、_wcsrev
、_mbsrev
、_mbsrev_l
的原始文档
http://msdn.microsoft.com/en-us/library/9hby7w40%28v=vs.80%29.aspx.