139 lines
5.9 KiB
C#
139 lines
5.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using LeanCloud.Storage.Internal;
|
|
|
|
namespace LeanCloud.Utilities
|
|
{
|
|
/// <summary>
|
|
/// A set of utilities for converting generic types between each other.
|
|
/// </summary>
|
|
public static class Conversion
|
|
{
|
|
/// <summary>
|
|
/// Converts a value to the requested type -- coercing primitives to
|
|
/// the desired type, wrapping lists and dictionaries appropriately,
|
|
/// or else returning null.
|
|
///
|
|
/// This should be used on any containers that might be coming from a
|
|
/// user to normalize the collection types. Collection types coming from
|
|
/// JSON deserialization can be safely assumed to be lists or dictionaries of
|
|
/// objects.
|
|
/// </summary>
|
|
public static T As<T>(object value) where T : class
|
|
{
|
|
return ConvertTo<T>(value) as T;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a value to the requested type -- coercing primitives to
|
|
/// the desired type, wrapping lists and dictionaries appropriately,
|
|
/// or else throwing an exception.
|
|
///
|
|
/// This should be used on any containers that might be coming from a
|
|
/// user to normalize the collection types. Collection types coming from
|
|
/// JSON deserialization can be safely assumed to be lists or dictionaries of
|
|
/// objects.
|
|
/// </summary>
|
|
public static T To<T>(object value)
|
|
{
|
|
return (T)ConvertTo<T>(value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a value to the requested type -- coercing primitives to
|
|
/// the desired type, wrapping lists and dictionaries appropriately,
|
|
/// or else passing the object along to the caller unchanged.
|
|
///
|
|
/// This should be used on any containers that might be coming from a
|
|
/// user to normalize the collection types. Collection types coming from
|
|
/// JSON deserialization can be safely assumed to be lists or dictionaries of
|
|
/// objects.
|
|
/// </summary>
|
|
internal static object ConvertTo<T>(object value)
|
|
{
|
|
if (value is T || value == null)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (ReflectionHelpers.IsPrimitive(typeof(T)))
|
|
{
|
|
return (T)Convert.ChangeType(value, typeof(T));
|
|
}
|
|
|
|
if (ReflectionHelpers.IsConstructedGenericType(typeof(T)))
|
|
{
|
|
// Add lifting for nullables. Only supports conversions between primitives.
|
|
if (ReflectionHelpers.IsNullable(typeof(T)))
|
|
{
|
|
var innerType = ReflectionHelpers.GetGenericTypeArguments(typeof(T))[0];
|
|
if (ReflectionHelpers.IsPrimitive(innerType))
|
|
{
|
|
return (T)Convert.ChangeType(value, innerType);
|
|
}
|
|
}
|
|
Type listType = GetInterfaceType(value.GetType(), typeof(IList<>));
|
|
var la = typeof(T).GetGenericTypeDefinition();
|
|
var ilb = typeof(IList<>);
|
|
var lb = typeof(List<>);
|
|
if (listType != null &&
|
|
(la == ilb || la == lb))
|
|
{
|
|
var wrapperType = typeof(FlexibleListWrapper<,>)
|
|
.MakeGenericType(ReflectionHelpers.GetGenericTypeArguments(typeof(T))[0],
|
|
ReflectionHelpers.GetGenericTypeArguments(listType)[0]);
|
|
return Activator.CreateInstance(wrapperType, value);
|
|
}
|
|
Type dictType = GetInterfaceType(value.GetType(), typeof(IDictionary<,>));
|
|
var da = typeof(T).GetGenericTypeDefinition();
|
|
var db = typeof(IDictionary<,>);
|
|
if (dictType != null &&
|
|
da == db)
|
|
{
|
|
var wrapperType = typeof(FlexibleDictionaryWrapper<,>)
|
|
.MakeGenericType(ReflectionHelpers.GetGenericTypeArguments(typeof(T))[1],
|
|
ReflectionHelpers.GetGenericTypeArguments(dictType)[1]);
|
|
return Activator.CreateInstance(wrapperType, value);
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Holds a dictionary that maps a cache of interface types for related concrete types.
|
|
/// The lookup is slow the first time for each type because it has to enumerate all interface
|
|
/// on the object type, but made fast by the cache.
|
|
///
|
|
/// The map is:
|
|
/// (object type, generic interface type) => constructed generic type
|
|
/// </summary>
|
|
private static readonly Dictionary<Tuple<Type, Type>, Type> interfaceLookupCache =
|
|
new Dictionary<Tuple<Type, Type>, Type>();
|
|
private static Type GetInterfaceType(Type objType, Type genericInterfaceType)
|
|
{
|
|
// Side note: It so sucks to have to do this. What a piece of crap bit of code
|
|
// Unfortunately, .NET doesn't provide any of the right hooks to do this for you
|
|
// *sigh*
|
|
if (ReflectionHelpers.IsConstructedGenericType(genericInterfaceType))
|
|
{
|
|
genericInterfaceType = genericInterfaceType.GetGenericTypeDefinition();
|
|
}
|
|
var cacheKey = new Tuple<Type, Type>(objType, genericInterfaceType);
|
|
if (interfaceLookupCache.ContainsKey(cacheKey))
|
|
{
|
|
return interfaceLookupCache[cacheKey];
|
|
}
|
|
foreach (var type in ReflectionHelpers.GetInterfaces(objType))
|
|
{
|
|
if (ReflectionHelpers.IsConstructedGenericType(type) &&
|
|
type.GetGenericTypeDefinition() == genericInterfaceType)
|
|
{
|
|
return interfaceLookupCache[cacheKey] = type;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
}
|