Skip to content

Instantly share code, notes, and snippets.

@controlflow
Last active August 29, 2015 14:25
Show Gist options
  • Save controlflow/53c4320364eae642be4e to your computer and use it in GitHub Desktop.
Save controlflow/53c4320364eae642be4e to your computer and use it in GitHub Desktop.
Simple System.Type visitor to traverse and replace types
using System;
using JetBrains.Annotations;
namespace Smth {
[PublicAPI]
public abstract class ReflectionTypeVisitor<T> {
public virtual T VisitSimpleType(Type type) => default(T);
public virtual T VisitConstructedType([NotNull] Type typeDefinition, [NotNull] Type[] typeArguments) => default(T);
public virtual T VisitTypeParameterType([NotNull] Type typeParameter) => default(T);
public virtual T VisitArrayType([NotNull] Type arrayType) => default(T);
public virtual T VisitByRefType([NotNull] Type referenceType) => default(T);
public virtual T VisitPointerType([NotNull] Type pointerType) => default(T);
protected T Accept([NotNull] Type type) {
if (type.IsArray) return VisitArrayType(type);
if (type.IsByRef) return VisitByRefType(type);
if (type.IsPointer) return VisitPointerType(type);
if (type.IsGenericParameter) return VisitTypeParameterType(type);
if (type.IsConstructedGenericType) {
return VisitConstructedType(
typeDefinition: type.GetGenericTypeDefinition(),
typeArguments: type.GetGenericArguments());
}
return VisitSimpleType(type);
}
}
}
using System;
using JetBrains.Annotations;
namespace Smth {
[PublicAPI]
public abstract class RewritingTypeVisitor : ReflectionTypeVisitor<Type> {
public override Type VisitSimpleType(Type type) => null;
public override Type VisitTypeParameterType(Type typeParameter) => null;
public override Type VisitConstructedType(Type typeDefinition, Type[] typeArguments) {
var newTypeDefinition = Accept(typeDefinition);
Type[] newTypeArguments = null;
for (var index = 0; index < typeArguments.Length; index++) {
var newTypeArgument = Accept(typeArguments[index]);
if (newTypeArgument == null) continue;
if (newTypeArguments == null) {
newTypeArguments = new Type[typeArguments.Length];
Array.Copy(typeArguments, newTypeArguments, typeArguments.Length);
}
newTypeArguments[index] = newTypeArgument;
}
if (newTypeDefinition == null && newTypeArguments == null) return null;
var definition = newTypeDefinition ?? typeDefinition;
return definition.MakeGenericType(newTypeArguments ?? typeArguments);
}
public override Type VisitArrayType(Type arrayType) {
var elementType = arrayType.GetElementType();
var newElementType = Accept(elementType);
if (newElementType == null) return null;
if (arrayType == elementType.MakeArrayType()) { // SZArray check
return newElementType.MakeArrayType();
}
return newElementType.MakeArrayType(arrayType.GetArrayRank());
}
public override Type VisitByRefType(Type referenceType) {
var newElementType = Accept(referenceType.GetElementType());
return newElementType?.MakeByRefType();
}
public override Type VisitPointerType(Type pointerType) {
var newElementType = Accept(pointerType.GetElementType());
return newElementType?.MakePointerType();
}
[NotNull, Pure]
public Type Rewrite([NotNull] Type type) {
return Accept(type) ?? type;
}
}
}
private sealed class ReplaceTypeVisitor : RewritingTypeVisitor {
[NotNull] private readonly Dictionary<Type, Type> myTypesMap;
public ReplaceTypeVisitor([NotNull] Dictionary<Type, Type> typesMap) {
myTypesMap = typesMap;
}
public override Type VisitSimpleType(Type type) {
Type value;
myTypesMap.TryGetValue(type, out value);
return value;
}
public override Type VisitTypeParameterType(Type typeParameter) {
return VisitSimpleType(typeParameter);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment