private object ConvertInternal(Type sourceType, Type targetType, object value, object defaultReturnValue = null, bool throwIfConvertFails = true) { Guard.ArgumentNotNull(value, nameof(value)); Guard.ArgumentNotNull(sourceType, nameof(sourceType)); Guard.ArgumentNotNull(targetType, nameof(targetType)); // If source type is the same as target type, no conversion/casting is necessary if (sourceType == targetType) { return(value); } // Try to read conversion method from cache var cachedValue = this.TryGetCachedValue(value, sourceType, targetType); if (cachedValue != null && cachedValue.IsSuccessful) { return(cachedValue.Value); } // Try to convert using defined sequence of conversion attempts foreach (var conversionAttempt in this.conversionAttempts) { var conversionResult = conversionAttempt.TryConvert(value, sourceType, targetType); if (conversionResult != null && conversionResult.IsSuccessful) { this.cacheManager.UpdateCache( sourceType: sourceType, targetType: targetType, isConvertable: conversionResult.IsSuccessful, conversionAttempt: conversionAttempt); return(conversionResult.Value); } } // If all fails, we either throw an exception if (throwIfConvertFails) { throw ConversionNotSupportedException.Create(sourceType, targetType); } // ...or return a default target value if (defaultReturnValue == null) { return(targetType.GetDefault()); } return(defaultReturnValue); }
private ConversionResult TryGetCachedValue(object value, Type sourceType, Type targetType) { var cacheResult = this.cacheManager.TryGetCachedValue(sourceType, targetType); if (cacheResult.IsCached) { if (cacheResult.IsConvertable) { var cachedAttempt = this.conversionAttempts.Single(a => a == cacheResult.ConversionAttempt); var convertedValue = cachedAttempt.TryConvert(value, sourceType, targetType); return(convertedValue); } return(new ConversionResult(ConversionNotSupportedException.Create(sourceType, targetType))); } return(null); }
internal static CastResult CastTo(object value, Type targetType) { if (value == null) { return(new CastResult((object)null, CastFlag.Undefined)); } Guard.ArgumentNotNull(targetType, nameof(targetType)); var sourceType = value.GetType(); var sourceTypeInfo = value.GetType().GetTypeInfo(); var targetTypeInfo = targetType.GetTypeInfo(); if (targetTypeInfo.IsGenericType && !targetTypeInfo.GenericTypeArguments.Any()) { return (new CastResult( ConversionNotSupportedException.Create( sourceType, targetType, string.Format("The target type {0} does not have sufficient generic type arguments specified.", targetType.GetFormattedName())), CastFlag.Undefined)); } CastResult castResult = null; CastFlag castFlag = CastFlag.Undefined; try { // explicit conversion always works if to : from OR if there's an implicit conversion if (targetType.IsSameOrParent(sourceType)) { castFlag = CastFlag.Implicit; var castedValue = GenericCast(() => AttemptImplicitCast <object, object>(null), sourceType, targetType, value); castResult = new CastResult(castedValue, castFlag); } // for nullable types, we can simply strip off the nullability and evaluate the underyling types else if (Nullable.GetUnderlyingType(targetType) != null) { castResult = CastTo(value, Nullable.GetUnderlyingType(targetType)); } else if (sourceTypeInfo.IsValueType) { castFlag = CastFlag.Explicit; var castedValue = GenericCast(() => AttemptExplicitCast <object, object>(null), sourceType, targetType, value); castResult = new CastResult(castedValue, castFlag); } else { // Implicit cast operators have priority in favour of explicit operators // since they should not lose precision. See C# language specification: // https://msdn.microsoft.com/en-us/library/z5z9kes2.aspx var conversionMethods = GetCastOperatorMethods(sourceType, targetType) .OrderByDescending(m => m.Name == "op_Implicit") .ThenByDescending(m => m.ReturnType == targetType || m.ReturnType.GetTypeInfo().IsAssignableFrom(targetTypeInfo)); foreach (var conversionMethod in conversionMethods) { try { var convertedValue = conversionMethod.Invoke(null, new[] { value }); castResult = CastTo(convertedValue, targetType); if (castResult.IsSuccessful) { break; } else { castResult = null; } } catch { } } } } catch (Exception ex) { castResult = new CastResult(ConversionNotSupportedException.Create(sourceType, targetType, ex.InnerException), castFlag); } if (castResult == null) { castResult = new CastResult(ConversionNotSupportedException.Create(sourceType, targetType), castFlag); } return(castResult); }