-
-
Save dburner/9ff9e9da7346cfa9d7f833232b5f6952 to your computer and use it in GitHub Desktop.
Devious C# class that makes .NET strings mutable without requiring the /unsafe compiler switch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class EvilStringHelper | |
{ | |
private static readonly Action<string, int, char> _setChar; | |
private static readonly Action<string, int> _setLength; | |
static EvilStringHelper() | |
{ | |
if (Environment.Version.Major < 4) | |
{ | |
MethodInfo setCharMethod = typeof(string).GetMethod( | |
"SetChar", | |
BindingFlags.Instance | BindingFlags.NonPublic | |
); | |
_setChar = (Action<string, int, char>)Delegate.CreateDelegate(typeof(Action<string, int, char>), setCharMethod); | |
MethodInfo setLengthMethod = typeof(string).GetMethod( | |
"SetLength", | |
BindingFlags.Instance | BindingFlags.NonPublic | |
); | |
_setLength = (Action<string, int>)Delegate.CreateDelegate(typeof(Action<string, int>), setLengthMethod); | |
} | |
else | |
{ | |
MethodInfo fillStringCheckedMethod = typeof(string).GetMethod( | |
"FillStringChecked", | |
BindingFlags.Static | BindingFlags.NonPublic | |
); | |
Action<string, int, string> fillStringCheckedDelegate = (Action<string, int, string>)Delegate.CreateDelegate( | |
typeof(Action<string, int, string>), fillStringCheckedMethod | |
); | |
_setChar = (str, i, c) => fillStringCheckedDelegate(str, i, c.ToString()); | |
FieldInfo stringLengthField = typeof(string).GetField( | |
"m_stringLength", | |
BindingFlags.Instance | BindingFlags.NonPublic | |
); | |
var input = Expression.Parameter(typeof(string), "input"); | |
var length = Expression.Parameter(typeof(int), "length"); | |
var setLengthLambda = Expression.Lambda<Action<string, int>>( | |
Expression.Assign(Expression.Field(input, stringLengthField), length), | |
input, | |
length | |
); | |
_setLength = setLengthLambda.Compile(); | |
} | |
} | |
public static void ChangeTo(this string text, string value) | |
{ | |
_setLength(text, value.Length); | |
for (int i = 0; i < value.Length; ++i) | |
text.SetChar(i, value[i]); | |
} | |
public static void SetChar(this string text, int index, char value) | |
{ | |
_setChar(text, index, value); | |
} | |
public static void ReverseInPlace(this string text) | |
{ | |
int i = 0; | |
int j = text.Length - 1; | |
while (i < j) | |
{ | |
char temp = text[j]; | |
_setChar(text, j--, text[i]); | |
_setChar(text, i++, temp); | |
} | |
} | |
public static unsafe string ReverseOutOfPlace(this string text) | |
{ | |
int length = text.Length; | |
char* reversed = stackalloc char[length]; | |
int i = 0, j = length - 1; | |
fixed (char* p = text) | |
{ | |
while (i < length) | |
{ | |
reversed[i++] = p[j--]; | |
} | |
} | |
return new string(reversed, 0, length); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment