This question has been answered sufficiently many times, but with C# 7.2 and the introduction of the Span type, there is a faster way to do this in unsafe code:
public static class StringSupport
{
private static readonly int _charSize = sizeof(char);
public static unsafe byte[] GetBytes(string str)
{
if (str == null) throw new ArgumentNullException(nameof(str));
if (str.Length == 0) return new byte[0];
fixed (char* p = str)
{
return new Span<byte>(p, str.Length * _charSize).ToArray();
}
}
public static unsafe string GetString(byte[] bytes)
{
if (bytes == null) throw new ArgumentNullException(nameof(bytes));
if (bytes.Length % _charSize != 0) throw new ArgumentException($"Invalid {nameof(bytes)} length");
if (bytes.Length == 0) return string.Empty;
fixed (byte* p = bytes)
{
return new string(new Span<char>(p, bytes.Length / _charSize));
}
}
}
Keep in mind that the bytes represent a UTF-16 encoded string (called "Unicode" in C# land).
Some quick benchmarking shows that the above methods are roughly 5x faster than their Encoding.Unicode.GetBytes(...)/GetString(...) implementations for medium sized strings (30-50 chars), and even faster for larger strings. These methods also seem to be faster than using pointers with Marshal.Copy(..) or Buffer.MemoryCopy(...).