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);
        }
Ejemplo n.º 3
0
        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);
        }