Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support abstract classes and classes with virtual members as the base type for the ducktype proxy #42

Merged
merged 10 commits into from
Jun 21, 2020
247 changes: 142 additions & 105 deletions README.md

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions src/Wanhjor.ObjectInspector/DuckType.Create.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public partial class DuckType
/// Create duck type proxy from an interface
/// </summary>
/// <param name="instance">Instance object</param>
/// <typeparam name="T">Interface type</typeparam>
/// <typeparam name="T">Duck type</typeparam>
/// <returns>Duck type proxy</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Create<T>(object instance)
Expand All @@ -21,15 +21,15 @@ public static T Create<T>(object instance)
/// <summary>
/// Create duck type proxy from an interface type
/// </summary>
/// <param name="interfaceType">Interface type</param>
/// <param name="duckType">Duck type</param>
/// <param name="instance">Instance object</param>
/// <returns>Duck Type proxy</returns>
public static DuckType Create(Type interfaceType, object instance)
public static DuckType Create(Type duckType, object instance)
{
EnsureArguments(interfaceType, instance);
EnsureArguments(duckType, instance);

// Create Type
var type = GetOrCreateProxyType(interfaceType, instance.GetType());
var type = GetOrCreateProxyType(duckType, instance.GetType());

// Create instance
var objInstance = (DuckType)FormatterServices.GetUninitializedObject(type);
Expand All @@ -40,12 +40,12 @@ public static DuckType Create(Type interfaceType, object instance)
/// <summary>
/// Create a duck type proxy from an interface type
/// </summary>
/// <param name="interfaceType">Interface type</param>
/// <param name="duckType">Duck type</param>
/// <param name="instanceType">Instance type</param>
/// <returns>Duck Type proxy</returns>
public static DuckType Create(Type interfaceType, Type instanceType)
public static DuckType Create(Type duckType, Type instanceType)
{
var type = GetOrCreateProxyType(interfaceType, instanceType);
var type = GetOrCreateProxyType(duckType, instanceType);
return (DuckType) FormatterServices.GetUninitializedObject(type);
}
}
Expand Down
20 changes: 10 additions & 10 deletions src/Wanhjor.ObjectInspector/DuckType.Factory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,38 @@ public partial class DuckType
/// Gets a ducktype factory for an interface and instance type
/// </summary>
/// <param name="instance">Object instance</param>
/// <typeparam name="T">Type of interface</typeparam>
/// <typeparam name="T">Type of Duck</typeparam>
/// <returns>Duck Type factory</returns>
public static IDuckTypeFactory<T> GetFactory<T>(object instance) where T:class
{
var interfaceType = typeof(T);
EnsureArguments(interfaceType, instance);
var duckType = typeof(T);
EnsureArguments(duckType, instance);

// Create Type
var type = GetOrCreateProxyType(interfaceType, instance.GetType());
var type = GetOrCreateProxyType(duckType, instance.GetType());
return new DuckTypeFactory<T>(type);
}
/// <summary>
/// Gets a ducktype factory for an interface and instance type
/// </summary>
/// <param name="interfaceType">Interface type</param>
/// <param name="duckType">Duck type</param>
/// <param name="instanceType">Object type</param>
/// <returns>Duck type factory</returns>
public static IDuckTypeFactory<object> GetFactoryByTypes(Type interfaceType, Type instanceType)
public static IDuckTypeFactory<object> GetFactoryByTypes(Type duckType, Type instanceType)
{
var type = GetOrCreateProxyType(interfaceType, instanceType);
var type = GetOrCreateProxyType(duckType, instanceType);
return new DuckTypeFactory<object>(type);
}
/// <summary>
/// Gets a ducktype factory for an interface and instance type
/// </summary>
/// <param name="instanceType">Type of instance</param>
/// <typeparam name="T">Type of interface</typeparam>
/// <typeparam name="T">Type of Duck</typeparam>
/// <returns>Duck Type factory</returns>
public static IDuckTypeFactory<T> GetFactoryByTypes<T>(Type instanceType) where T:class
{
var interfaceType = typeof(T);
var type = GetOrCreateProxyType(interfaceType, instanceType);
var duckType = typeof(T);
var type = GetOrCreateProxyType(duckType, instanceType);
return new DuckTypeFactory<T>(type);
}

Expand Down
8 changes: 4 additions & 4 deletions src/Wanhjor.ObjectInspector/DuckType.Fields.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ private static MethodBuilder GetFieldGetMethod(Type instanceType, TypeBuilder ty
PropertyInfo iProperty, FieldInfo field, FieldInfo instanceField)
{
var method = typeBuilder.DefineMethod("get_" + iProperty.Name,
MethodAttributes.Public | MethodAttributes.SpecialName |
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Final |
MethodAttributes.HideBySig | MethodAttributes.Virtual,
iProperty.PropertyType, Type.EmptyTypes);

Expand All @@ -23,7 +23,7 @@ private static MethodBuilder GetFieldGetMethod(Type instanceType, TypeBuilder ty
var iPropTypeInterface = iProperty.PropertyType;
if (iPropTypeInterface.IsGenericType)
iPropTypeInterface = iPropTypeInterface.GetGenericTypeDefinition();
if (iProperty.PropertyType != field.FieldType && iProperty.PropertyType.IsInterface && field.FieldType.GetInterface(iPropTypeInterface.FullName) == null)
if (iProperty.PropertyType != field.FieldType && !iProperty.PropertyType.IsValueType && !iProperty.PropertyType.IsAssignableFrom(field.FieldType))
{
if (field.IsStatic)
{
Expand Down Expand Up @@ -93,7 +93,7 @@ private static MethodBuilder GetFieldSetMethod(Type instanceType, TypeBuilder ty
PropertyInfo iProperty, FieldInfo field, FieldInfo instanceField)
{
var method = typeBuilder.DefineMethod("set_" + iProperty.Name,
MethodAttributes.Public | MethodAttributes.SpecialName |
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Final |
MethodAttributes.HideBySig | MethodAttributes.Virtual,
typeof(void),
new[] {iProperty.PropertyType});
Expand Down Expand Up @@ -125,7 +125,7 @@ private static MethodBuilder GetFieldSetMethod(Type instanceType, TypeBuilder ty
var iPropTypeInterface = iProperty.PropertyType;
if (iPropTypeInterface.IsGenericType)
iPropTypeInterface = iPropTypeInterface.GetGenericTypeDefinition();
if (iProperty.PropertyType != field.FieldType && iProperty.PropertyType.IsInterface && field.FieldType.GetInterface(iPropTypeInterface.FullName) == null)
if (iProperty.PropertyType != field.FieldType && !iProperty.PropertyType.IsValueType && !iProperty.PropertyType.IsAssignableFrom(field.FieldType))
{
if (field.IsStatic)
{
Expand Down
41 changes: 31 additions & 10 deletions src/Wanhjor.ObjectInspector/DuckType.Methods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,44 @@ namespace Wanhjor.ObjectInspector
{
public partial class DuckType
{
private static void CreateInterfaceMethods(Type interfaceType, Type instanceType, FieldInfo instanceField, TypeBuilder typeBuilder)
private static List<MethodInfo> GetMethods(Type baseType)
{
var interfaceMethods = new List<MethodInfo>(interfaceType.GetMethods().Where(m => !m.IsSpecialName));
var implementedInterfaces = interfaceType.GetInterfaces();
var selectedMethods = new List<MethodInfo>(GetBaseMethods(baseType));
var implementedInterfaces = baseType.GetInterfaces();
foreach (var imInterface in implementedInterfaces)
{
if (imInterface == typeof(IDuckType)) continue;
var newMethods = imInterface.GetMethods()
.Where(m => !m.IsSpecialName && interfaceMethods.All(i => i.ToString() != m.ToString()));
interfaceMethods.AddRange(newMethods);
.Where(m => !m.IsSpecialName && selectedMethods.All(i => i.ToString() != m.ToString()));
selectedMethods.AddRange(newMethods);
}
foreach (var iMethod in interfaceMethods)
return selectedMethods;
static IEnumerable<MethodInfo> GetBaseMethods(Type baseType)
{
foreach (var method in baseType.GetMethods())
{
if (method.IsSpecialName || method.DeclaringType == typeof(DuckType))
continue;
if (baseType.IsInterface || method.IsAbstract || method.IsVirtual)
yield return method;
}
}
}

private static void CreateMethods(Type baseType, Type instanceType, FieldInfo instanceField, TypeBuilder typeBuilder)
{
var selectedMethods = GetMethods(baseType);
foreach (var iMethod in selectedMethods)
{
var iMethodParameters = iMethod.GetParameters();
var iMethodParametersTypes = iMethodParameters.Select(p => p.ParameterType).ToArray();

var attributes = iMethod.IsAbstract || iMethod.IsVirtual
? MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig
: MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot;

var paramBuilders = new ParameterBuilder[iMethodParameters.Length];
var methodBuilder = typeBuilder.DefineMethod(iMethod.Name,
MethodAttributes.Public | MethodAttributes.Virtual |
MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot,
var methodBuilder = typeBuilder.DefineMethod(iMethod.Name, attributes,
iMethod.ReturnType, iMethodParametersTypes);

var iMethodGenericArguments = iMethod.GetGenericArguments();
Expand Down Expand Up @@ -62,7 +80,10 @@ private static void CreateInterfaceMethods(Type interfaceType, Type instanceType
var iMethodReturnType = iMethod.ReturnType;
if (iMethodReturnType.IsGenericType)
iMethodReturnType = iMethodReturnType.GetGenericTypeDefinition();
if (iMethod.ReturnType != method.ReturnType && iMethod.ReturnType.IsInterface && method.ReturnType.GetInterface(iMethodReturnType.FullName) == null)

if (iMethod.ReturnType != method.ReturnType &&
!iMethod.ReturnType.IsValueType && !iMethod.ReturnType.IsAssignableFrom(method.ReturnType) &&
!iMethod.ReturnType.IsGenericParameter && !method.ReturnType.IsGenericParameter)
{
il.Emit(OpCodes.Ldtoken, iMethod.ReturnType);
il.EmitCall(OpCodes.Call, GetTypeFromHandleMethodInfo, null);
Expand Down
10 changes: 6 additions & 4 deletions src/Wanhjor.ObjectInspector/DuckType.Properties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ private static MethodBuilder GetPropertyGetMethod(Type instanceType, TypeBuilder
{
var parameterTypes = GetPropertyParameterTypes(iProperty, false);
var method = typeBuilder.DefineMethod("get_" + iProperty.Name,
MethodAttributes.Public | MethodAttributes.SpecialName |
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Final |
MethodAttributes.HideBySig | MethodAttributes.Virtual,
iProperty.PropertyType, parameterTypes);
var il = method.GetILGenerator();
Expand All @@ -45,7 +45,8 @@ private static MethodBuilder GetPropertyGetMethod(Type instanceType, TypeBuilder
var iPropTypeInterface = iProperty.PropertyType;
if (iPropTypeInterface.IsGenericType)
iPropTypeInterface = iPropTypeInterface.GetGenericTypeDefinition();
if (iProperty.PropertyType != prop.PropertyType && parameterTypes.Length == 0 && iProperty.PropertyType.IsInterface && prop.PropertyType.GetInterface(iPropTypeInterface.FullName) == null)
if (iProperty.PropertyType != prop.PropertyType && parameterTypes.Length == 0 &&
!iProperty.PropertyType.IsValueType && !iProperty.PropertyType.IsAssignableFrom(prop.PropertyType))
{
if (propMethod.IsStatic)
{
Expand Down Expand Up @@ -146,7 +147,7 @@ private static MethodBuilder GetPropertySetMethod(Type instanceType, TypeBuilder
{
var parameterTypes = GetPropertyParameterTypes(iProperty, true);
var method = typeBuilder.DefineMethod("set_" + iProperty.Name,
MethodAttributes.Public | MethodAttributes.SpecialName |
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Final |
MethodAttributes.HideBySig | MethodAttributes.Virtual,
typeof(void),
parameterTypes);
Expand All @@ -171,7 +172,8 @@ private static MethodBuilder GetPropertySetMethod(Type instanceType, TypeBuilder
var iPropTypeInterface = iProperty.PropertyType;
if (iPropTypeInterface.IsGenericType)
iPropTypeInterface = iPropTypeInterface.GetGenericTypeDefinition();
if (iProperty.PropertyType != prop.PropertyType && parameterTypes.Length == 1 && iProperty.PropertyType.IsInterface && prop.PropertyType.GetInterface(iPropTypeInterface.FullName) == null)
if (iProperty.PropertyType != prop.PropertyType && parameterTypes.Length == 1 &&
!iProperty.PropertyType.IsValueType && !iProperty.PropertyType.IsAssignableFrom(prop.PropertyType))
{
if (propMethod.IsStatic)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Wanhjor.ObjectInspector/DuckType.Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ private static void EnsureArguments(Type interfaceType, object instance)
throw new ArgumentNullException(nameof(interfaceType), "The interface type can't be null");
if (instance is null)
throw new ArgumentNullException(nameof(instance), "The object instance can't be null");
if (!interfaceType.IsInterface)
throw new DuckTypeTypeIsNotAnInterfaceException(interfaceType, nameof(interfaceType));
//if (!interfaceType.IsInterface)
// throw new DuckTypeTypeIsNotAnInterfaceException(interfaceType, nameof(interfaceType));
if (!interfaceType.IsPublic && !interfaceType.IsNestedPublic)
throw new DuckTypeTypeIsNotPublicException(interfaceType, nameof(interfaceType));
}
Expand Down
Loading