This is something that went into being as a result of an interesting “Q” faced by a close associate as part of his technical interview. The “Q” being - How would one represent a String as a value type in DotNET to which his answer was an immediate “No, you can’t!” Theoretically he is correct in DotNET realms and nothing ordinary would mandate you to even think about it. I guess Mr. Hyde got better off and sent me reeling on such a realization (but still not sure if this was what the interviewer had in mind) to this “Q”.
This has nothing special to pay attention to apart from usage of certain CSharp (C#) language features like Dynamic code generation using Reflection, Extension Methods, LINQ, Anonymous functions etc. TypeAttributes.SequentialLayout (Highlighted line) does have lot of significance the way the structure is created here!
The major short-coming would be the structure itself for String encoding. It would have been ideal to have the structure to just have one field each for every single character and the corresponding positions. The use of bit array was explored but abandoned due to its cap at 32 & 64. I didn’t really spend much time on getting the right structure as this was more an adventure to somehow try and see if it was really possible as opposed to finding a smart way. Any other intelligent encoding schemes or data compression techniques (Huffman coding, Arithmetic coding, LZW coding etc.) would really uplift this adventure to a different level. The journey itself is the reward here!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
namespace StringVal
{
public static class StringHelper
{
/// <summary>
/// Returns the Value Type equivalent of a String.
/// </summary>
public static ValueType GetValue(this string s)
{
int _stringLength = s.Length;
string _typeName = "StringVal";
AssemblyName _assemblyName = new AssemblyName();
_assemblyName.Name = "IamOnTopOfTheWorld";
AssemblyBuilder _newAssembly =
System.Threading.Thread.GetDomain().DefineDynamicAssembly(_assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder _newModule = _newAssembly.DefineDynamicModule("StringUtils");
TypeBuilder _newType = _newModule.DefineType(_typeName,
TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.SequentialLayout | TypeAttributes.Serializable,
typeof(ValueType));
FieldBuilder field;
for (int i=0;i<_stringLength;i++)
{
field = _newType.DefineField(string.Format("c{0}_{1}", i, Char.ConvertToUtf32(s, i)), typeof(bool), FieldAttributes.Public);
}
Type type = _newType.CreateType();
return (ValueType)_newAssembly.CreateInstance(_typeName);
}
/// <summary>
/// Returns the String equivalent of Type :: StringVal
/// </summary>
public static string GetString(this ValueType valType)
{
string sOutput = @"Error::Expected Value Type-""StringVal""";
if (valType.GetType().Assembly.GetName().Name.Equals("IamOnTopOfTheWorld") &&
valType.GetType().Module.ScopeName.Equals("StringUtils") &&
valType.GetType().Name.Equals("StringVal"))
{
var query = from m in valType.GetType().GetFields()
select m;
StringBuilder sBuilder = new StringBuilder(query.Count<FieldInfo>());
string fldName;
int utfCharCode;
foreach (FieldInfo fldInfo in query)
{
fldName = fldInfo.Name;
utfCharCode = int.Parse(fldName.Substring(fldName.IndexOf("_") + 1));
sBuilder = sBuilder.Append(Char.ConvertFromUtf32(utfCharCode));
}
sOutput = sBuilder.ToString();
}
return sOutput;
}
/// <summary>
/// Returns the byte size of a structure whose type is 'type', as stored in managed memory.
/// Courtesy: <a href="http://msdn.microsoft.com/en-us/library/eahchzkf.aspx">MSDN</a>
/// </summary>
public static int GetManagedSize(this Type type)
{
var method = new DynamicMethod("GetManagedSizeImpl",
typeof(uint),
new Type[0],
false);
ILGenerator gen = method.GetILGenerator();
gen.Emit(OpCodes.Sizeof, type);
gen.Emit(OpCodes.Ret);
var func = (Func<uint>)method.CreateDelegate(typeof(Func<uint>));
return checked((int)func());
}
}
/// <summary>
/// Test Harness!
/// </summary>
class Program
{
static void Main(string[] args)
{
string x = @"Mary had a little lamb!";//Provide your Input String here...
Console.WriteLine(String.Format(@"Initialized String -> {0}",x));
ValueType y = x.GetValue();//Converts the string to a value type
Console.WriteLine(String.Format(@"String Converted to Value of Type -> {0} || Size -> {1} bytes",
y.GetType().Name,
y.GetType().GetManagedSize()));
Console.WriteLine(String.Format(@"Recovered String from it's equivalent Value Type -> {0}",
y.GetString()));//Converts the value type to String
Console.Read();
}
}
}
You know what, when some one would have asked this question, he/ she would not have thought of that it would result into this kind of utility class.
The interview question was just to judge if the guy knows that string is a reference type and not a value type and immutable I guess
.
Exactly Gaurave! Like I said, it is quite unnatural to think otherwise!
But sometimes you just can’t help but tread the un-trodden! Like I said the journey itself was the reward here.
Plus it led to an internal interesting debate/discussion thread on “ValueType” class itself (I believe Renju had already shared this with you).
Thank you for dropping a comment! I see that you like puzzles (myself a big fan…did try to blog something on those lines as well sometime back…but not an avid blogger).
Coool…
This mammoth utility class resulted from not understanding the real intention of interviewer!
Wait..I know a similar story…
when Hanuman finds himself unable to identify the herb[Sanjivani], he lifts the entire mountain and delivers it to the battlefield in Lanka..
Wow what an analogy my friend!
Wonder if Hanuman would have qualified the interview here for donning Mr. Weirdo’s hat!