Skip to content

Instantly share code, notes, and snippets.

@JimmyCushnie
Created September 14, 2020 15:01
Show Gist options
  • Save JimmyCushnie/950e09d9f96fab16d7d723c5b1583c3a to your computer and use it in GitHub Desktop.
Save JimmyCushnie/950e09d9f96fab16d7d723c5b1583c3a to your computer and use it in GitHub Desktop.
Turn C# type variables into and out of strings that look like C# code.
// These are methods for turning C# type variables into and out of strings that look like C# code.
// It has full support for generics (including unbound generics) and arrays of all dimensions.
// Examples of strings generated/parsed:
// System.Int32
// System.Collections.Generic.List<System.Int32>
// System.Collections.Generic.List<>
// System.Int32[]
// System.Int32[,,]
// System.Collections.Generic.Dictionary<System.String, System.Collections.Generic.List<System.Int32[,,]>>
// This code was written by me, Jimmy Cushnie, for my human-readable serialization library SUCC: https://github.com/JimmyCushnie/SUCC
// I hereby release it into the public domain:
/*
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public class TypeStrings
{
public static string GetCsharpTypeName(Type type)
{
if (type.IsGenericType)
{
string typeNameWithoutGenerics = type.FullName.Split('`')[0];
string genericArguments = String.Join(", ", type.GetGenericArguments().Select(GetCsharpTypeName));
return typeNameWithoutGenerics + "<" + genericArguments + ">";
}
else
{
return type.FullName;
}
}
public static Type ParseCsharpTypeName(string typeName)
{
if (!typeName.Contains('<'))
return ParseTypeFromAssemblyName(typeName);
var typeStack = new Stack<ParsingType>();
typeStack.Push(new ParsingType());
int previousTypeNameStartIndex = 0;
bool withinArrayDeclaration = false;
for (int i = 0; i < typeName.Length; i++)
{
switch (typeName[i])
{
case '<':
typeNameFinished();
addGenericParameterToStack();
break;
case '>':
typeNameFinished();
typeStack.Pop();
break;
case ',':
if (withinArrayDeclaration) // For example: List<int[,,]>
break;
typeNameFinished();
typeStack.Pop();
addGenericParameterToStack();
break;
case '[':
withinArrayDeclaration = true;
break;
case ']':
withinArrayDeclaration = false;
break;
}
void typeNameFinished()
{
var subTypeName =
typeName.Substring(previousTypeNameStartIndex, i - previousTypeNameStartIndex)
.Trim('<', '>', ',', ' ');
if (subTypeName.Length > 0)
typeStack.Peek().TypeFullName = subTypeName;
previousTypeNameStartIndex = i;
}
void addGenericParameterToStack()
{
var genericParameter = new ParsingType();
typeStack.Peek().GenericParameters.Add(genericParameter);
typeStack.Push(genericParameter);
}
}
if (typeStack.Count != 1)
throw new Exception("Aw fuck, looks like the type name had some non-matching brackets");
return typeStack.Pop().TurnIntoType();
}
private class ParsingType
{
public string TypeFullName;
public List<ParsingType> GenericParameters = new List<ParsingType>();
public Type TurnIntoType()
{
if (GenericParameters.Count == 0)
return ParseTypeFromAssemblyName(TypeFullName);
var genericTypeBase = ParseTypeFromAssemblyName(TypeFullName + "`" + GenericParameters.Count);
if (String.IsNullOrEmpty(GenericParameters[0].TypeFullName))
return genericTypeBase;
var genericParameterTypes = GenericParameters.Select(t => t.TurnIntoType()).ToArray();
return genericTypeBase.MakeGenericType(genericParameterTypes);
}
}
private static Type ParseTypeFromAssemblyName(string typeName)
{
foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies())
{
var type = ass.GetType(typeName);
if (type != null)
return type;
}
throw new FormatException($"cannot parse text {typeName} as System.Type");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment