Skip to content

Commit

Permalink
Update DefaultBinder to directly set if Type.IsAssignableFrom is true…
Browse files Browse the repository at this point in the history
… - greatly simplifies the code

Remove IsCollectionOrArray extension method, use the long hand - makes the code more readable.
Switch to using Activator.CreateInstance directly
Remove a large number of unused extensions in ModelBindingExtensions
Alternate to cefsharp#2052
  • Loading branch information
amaitland committed May 30, 2017
1 parent e94f063 commit 0d48e30
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 226 deletions.
39 changes: 18 additions & 21 deletions CefSharp/ModelBinding/DefaultBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,14 @@ public virtual object Bind(object obj, Type modelType)
return null;
}

//If the object can be directly assigned to the modelType then return immediately.
if(modelType.IsAssignableFrom(obj.GetType()))
{
return obj;
}

Type genericType = null;
if (modelType.IsCollectionOrArray())
if (modelType.IsCollection() || modelType.IsArray() || modelType.IsEnumerable())
{
//make sure it has a generic type
if (modelType.GetTypeInfo().IsGenericType)
Expand All @@ -70,13 +76,15 @@ public virtual object Bind(object obj, Type modelType)

if (genericType == null)
{
throw new ArgumentException("When modelType is an enumerable it must specify the type.", "modelType");
//If we don't have a generic type then just use object
genericType = typeof(object);
}
}

var bindingContext = this.CreateBindingContext(obj, modelType, genericType);
var destinationType = bindingContext.DestinationType;

if (bindingContext.DestinationType.IsCollectionOrArray())
if (destinationType.IsCollection() || destinationType.IsArray() || destinationType.IsEnumerable())
{
var model = (IList)bindingContext.Model;
var collection = obj as ICollection;
Expand Down Expand Up @@ -144,29 +152,18 @@ protected virtual void BindValue(BindingMemberInfo modelProperty, object obj, Bi
return;
}

Type dictionaryType = typeof(Dictionary<string, object>);

//If the type is a dictionary and the PropertyType isn't then we'll bind.
if (obj.GetType() == dictionaryType && modelProperty.PropertyType != dictionaryType)
if (modelProperty.PropertyType.IsAssignableFrom(obj.GetType()))
{
//We have a sub dictionary, attempt to bind it to the class
var model = Bind(obj, modelProperty.PropertyType);

modelProperty.SetValue(context.Model, model);
//Simply set the property
modelProperty.SetValue(context.Model, obj);
}
//If both types are collections then we'll bind
else if (obj.GetType().IsCollectionOrArray() && modelProperty.PropertyType.IsCollectionOrArray())
else
{
//We have a sub dictionary, attempt to bind it to the class
//Cannot directly set the property attempt to bind
var model = Bind(obj, modelProperty.PropertyType);

modelProperty.SetValue(context.Model, model);
}
else
{
//Simply set the property
modelProperty.SetValue(context.Model, obj);
}
}

protected virtual IEnumerable<BindingMemberInfo> GetBindingMembers(Type modelType, Type genericType)
Expand All @@ -178,14 +175,14 @@ protected virtual IEnumerable<BindingMemberInfo> GetBindingMembers(Type modelTyp

protected virtual object CreateModel(Type modelType, Type genericType)
{
if (modelType.IsCollectionOrArray())
if (modelType.IsCollection() || modelType.IsArray() || modelType.IsEnumerable())
{
//else just make a list
var listType = typeof(List<>).MakeGenericType(genericType);
return Activator.CreateInstance(listType);
}

return modelType.CreateInstance(true);
return Activator.CreateInstance(modelType, true);
}

protected virtual object GetValue(string propertyName, BindingContext context)
Expand Down
206 changes: 1 addition & 205 deletions CefSharp/ModelBinding/ModelBindingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,86 +14,21 @@ namespace CefSharp.ModelBinding
/// </summary>
internal static class ModelBindingExtensions
{
/// <summary>
/// Creates an instance of <paramref name="type"/> and cast it to <typeparamref name="T"/>.
/// </summary>
/// <param name="type">The type to create an instance of.</param>
/// <param name="nonPublic"><see langword="true"/> if a non-public constructor can be used, otherwise <see langword="false"/>.</param>
public static T CreateInstance<T>(this Type type, bool nonPublic = false)
{
if (!typeof(T).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
{
throw new InvalidOperationException("Unable to create instance of " + type.GetTypeInfo().FullName + "since it can't be cast to " + typeof(T).GetTypeInfo().FullName);
}

return (T)CreateInstance(type, nonPublic);
}

/// <summary>
/// Creates an instance of <paramref name="type"/>.
/// </summary>
/// <param name="type">The type to create an instance of.</param>
/// <param name="nonPublic"><see langword="true"/> if a non-public constructor can be used, otherwise <see langword="false"/>.</param>
public static object CreateInstance(this Type type, bool nonPublic = false)
{
return CreateInstanceInternal(type, nonPublic);
}

/// <summary>
/// returns the assembly that the type belongs to
/// </summary>
/// <param name="source"></param>
/// <returns> The assembly that contains the type </returns>
public static Assembly GetAssembly(this Type source)
{
return source.GetTypeInfo().Assembly;
}

/// <summary>
/// Checks if a type is an array or not
/// </summary>
/// <param name="source">The type to check.</param>
/// <returns><see langword="true" /> if the type is an array, otherwise <see langword="false" />.</returns>
public static bool IsArray(this Type source)
{

return source.GetTypeInfo().BaseType == typeof(Array);
}

/// <summary>
/// Is the type a collection, array or enumerable
/// </summary>
/// <param name="source">source type</param>
/// <returns>return if collection, array or enumerable</returns>
public static bool IsCollectionOrArray(this Type source)
{
return source.IsCollection() || source.IsArray() || source.IsEnumerable();
}

/// <summary>
/// Determines whether the <paramref name="genericType"/> is assignable from
/// <paramref name="givenType"/> taking into account generic definitions
/// </summary>
/// <remarks>
/// Borrowed from: http://tmont.com/blargh/2011/3/determining-if-an-open-generic-type-isassignablefrom-a-type
/// </remarks>
public static bool IsAssignableToGenericType(this Type givenType, Type genericType)
{
if (givenType == null || genericType == null)
{
return false;
}
return givenType == genericType
|| givenType.MapsToGenericTypeDefinition(genericType)
|| givenType.HasInterfaceThatMapsToGenericTypeDefinition(genericType)
|| givenType.GetTypeInfo().BaseType.IsAssignableToGenericType(genericType);
}

/// <summary>
/// Checks if a type is an collection or not
/// </summary>
/// <param name="source">The type to check.</param>
/// <returns><see langword="true" /> if the type is an collection, otherwise <see langword="false" />.</returns>
/// <returns><see langword="true" /> if the type is a collection, otherwise <see langword="false" />.</returns>
public static bool IsCollection(this Type source)
{
var collectionType = typeof(ICollection<>);
Expand All @@ -114,144 +49,5 @@ public static bool IsEnumerable(this Type source)

return source.GetTypeInfo().IsGenericType && source.GetGenericTypeDefinition() == enumerableType;
}

/// <summary>
/// Determines if a type is numeric. Nullable numeric types are considered numeric.
/// </summary>
/// <remarks>
/// Boolean is not considered numeric.
/// </remarks>
public static bool IsNumeric(this Type source)
{
if (source == null)
{
return false;
}

var underlyingType = Nullable.GetUnderlyingType(source) ?? source;

if (underlyingType.GetTypeInfo().IsEnum)
{
return false;
}

switch (underlyingType.GetTypeCode())
{
case TypeCode.Byte:
case TypeCode.Decimal:
case TypeCode.Double:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.SByte:
case TypeCode.Single:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return true;
default:
return false;
}
}

/// <summary>
/// Filters our all types not assignable to <typeparamref name="TType"/>.
/// </summary>
/// <typeparam name="TType">The type that all resulting <see cref="Type"/> should be assignable to.</typeparam>
/// <param name="types">An <see cref="IEnumerable{T}"/> of <see cref="Type"/> instances that should be filtered.</param>
/// <returns>An <see cref="IEnumerable{T}"/> of <see cref="Type"/> instances.</returns>
public static IEnumerable<Type> NotOfType<TType>(this IEnumerable<Type> types)
{
return types.Where(t => !typeof(TType).IsAssignableFrom(t));
}

private static bool HasInterfaceThatMapsToGenericTypeDefinition(this Type givenType, Type genericType)
{
return givenType
.GetInterfaces()
.Where(it => it.GetTypeInfo().IsGenericType)
.Any(it => it.GetGenericTypeDefinition() == genericType);
}

private static bool MapsToGenericTypeDefinition(this Type givenType, Type genericType)
{
return genericType.GetTypeInfo().IsGenericTypeDefinition
&& givenType.GetTypeInfo().IsGenericType
&& givenType.GetGenericTypeDefinition() == genericType;
}

public static TypeCode GetTypeCode(this Type type)
{
if (type == typeof(bool))
return TypeCode.Boolean;
else if (type == typeof(char))
return TypeCode.Char;
else if (type == typeof(sbyte))
return TypeCode.SByte;
else if (type == typeof(byte))
return TypeCode.Byte;
else if (type == typeof(short))
return TypeCode.Int16;
else if (type == typeof(ushort))
return TypeCode.UInt16;
else if (type == typeof(int))
return TypeCode.Int32;
else if (type == typeof(uint))
return TypeCode.UInt32;
else if (type == typeof(long))
return TypeCode.Int64;
else if (type == typeof(ulong))
return TypeCode.UInt64;
else if (type == typeof(float))
return TypeCode.Single;
else if (type == typeof(double))
return TypeCode.Double;
else if (type == typeof(decimal))
return TypeCode.Decimal;
else if (type == typeof(DateTime))
return TypeCode.DateTime;
else if (type == typeof(string))
return TypeCode.String;
else if (type.GetTypeInfo().IsEnum)
return GetTypeCode(Enum.GetUnderlyingType(type));
else
return TypeCode.Object;
}

private static object CreateInstanceInternal(Type type, bool nonPublic = false)
{
var constructor = type.GetDefaultConstructor(nonPublic);

if (constructor == null)
{
throw new MissingMethodException("No parameterless constructor defined for this object.");
}

return constructor.Invoke(new object[0]);
}

private static ConstructorInfo GetDefaultConstructor(this Type type, bool nonPublic = false)
{
var typeInfo = type.GetTypeInfo();

var constructors = typeInfo.DeclaredConstructors;

foreach (var constructor in constructors)
{
var parameters = constructor.GetParameters();

if (parameters.Length > 0)
{
continue;
}

if (!constructor.IsPrivate || nonPublic)
{
return constructor;
}
}

return null;
}
}
}

0 comments on commit 0d48e30

Please sign in to comment.