internal static bool AreConvertible(Type source, Type destination) { var nullableSource = NullableTypeHelper.TryGetNullableType(source); source = nullableSource ?? source; var nullableDestination = NullableTypeHelper.TryGetNullableType(destination); destination = nullableDestination ?? destination; return(AreExplicitlyConvertible(source, destination) || AreImplicitlyConvertible(source, destination) || CanConvertToOrFromEnum(source, destination)); }
/// <summary> /// Returns a type mapping of the TypePair you pass in. /// </summary> /// <returns></returns> private ProposedTypeMapping GetTypeMapping(int currentDepth, TypePair pair, MemberOptions options = null, CustomMapping customMapping = null) { if (!typeStack.Contains(pair)) { typeStack.Push(pair); } else if (this.options.Safety.IfRecursiveRelationshipIsDetected == RecursivePropertyOptions.ThrowIfRecursionIsDetected) { // Oh noes, recursion! throw new RecursiveRelationshipException(pair); } // if it's a recursive relationship, by default we return null which is handled after the method returns else { return(null); } var typeMapping = new ProposedTypeMapping(); typeMapping.SourceMember = null; typeMapping.DestinationMember = null; Type destinationType, sourceType; // If it's an enumerable type (List<>, IEnumerable<>, etc) then we're currently interested // in the type 'inside' the enumerable. if (CollectionTypeHelper.IsEnumerable(pair.DestinationType)) { destinationType = CollectionTypeHelper.GetTypeInsideEnumerable(pair.DestinationType); } else { destinationType = pair.DestinationType; } // Same here. if (CollectionTypeHelper.IsEnumerable(pair.SourceType)) { sourceType = CollectionTypeHelper.GetTypeInsideEnumerable(pair.SourceType); } else { sourceType = pair.SourceType; } // The memberprovider is responsible for linking a destination member with a source member var memberProvider = this.strategy.MemberProviderFactory.GetMemberProvider(sourceType, destinationType, mapper); // Loop through all members it could find foreach (var mapping in GetTypeMembers(memberProvider, options, currentDepth)) { var destinationMember = mapping.Destination; var sourceMember = mapping.Source; // Does the memberprovider see any reason to ignore this member? if (memberProvider.IsMemberIgnored(sourceType, destinationMember)) { continue; } Expression customExpression = null; // Try to extract an expression that was supplied for this destination member if (customMapping != null) { customExpression = customMapping.GetExpressionForMember(destinationMember); if (mapping.ConversionFunction == null) { var conversionFunction = customMapping.GetConversionFunction(sourceMember, destinationMember); mapping.ConversionFunction = conversionFunction; } } // Did the user supply a function to transform the source member's value? if (mapping.ConversionFunction != null) { // If no custom mapping yet, then we need to create one // as it's where we'll be storing the conversion function if (customMapping == null) { customMapping = new CustomMapping { DestinationType = destinationType }; customMappingCache.AddOrUpdate(pair, customMapping, (k, v) => customMapping); typeMapping.CustomMapping = customMapping; } // Let the custom mapping be the owner of the conversion function customMapping.AddConversionFunction(sourceMember, destinationMember, mapping.ConversionFunction); } else if (customMapping != null) { } ProposedHierarchicalMapping hierarchicalMapping = null; // No source member or can't write to the destination? if (HasNoSourceMember(customExpression, sourceMember) || !destinationMember.CanWrite) { if (this.options.Conventions.AutomaticallyFlattenHierarchies) { // Propose a mapping that flattens a hierarchy if possible. // For example, map type.CompanyName to otherType.Company.Name hierarchicalMapping = memberProvider.ProposeHierarchicalMapping(destinationMember); } // No way to map this thing? Add it to incompatible members if the option has been turned on. // Will cause an (intended) exception later on, allowing you to verify your mappings // for correctness and completeness. if (hierarchicalMapping == null && this.options.Strictness.ThrowWithoutCorrespondingSourceMember) { typeMapping.IncompatibleMappings.Add(destinationMember); } } // Nullable value types screw up everything! var nullableType = NullableTypeHelper.TryGetNullableType(sourceMember); // Can we do a simple right to left assignment between the members? // So, are they basically the same type or do we need to do further mapping? var canUseSimpleTypeMapping = CanUseDirectAssignment(pair, destinationMember, sourceMember, nullableType, hierarchicalMapping); if (canUseSimpleTypeMapping) { // If simple mapping is possible create a mapping between the members typeMapping.ProposedMappings.Add ( new ProposedMemberMapping { SourceMember = sourceMember, DestinationMember = destinationMember, HierarchicalMapping = hierarchicalMapping } ); } // No simple assignment, but a custom expression is supplied // and that's just as good as having a direct assignment mapping else if (customExpression != null || mapping.ConversionFunction != null) { typeMapping.ProposedMappings.Add ( new ProposedMemberMapping { SourceMember = sourceMember, DestinationMember = destinationMember, HierarchicalMapping = hierarchicalMapping } ); } // We have a source member but can't directly assign the source to the destination. // Further mapping is needed. else if (sourceMember != null) { // Is the member of an IEnumerable type? if (AreMembersIEnumerable(destinationMember, sourceMember)) { // Create a deeper mapping for IEnumerable members GenerateEnumerableMapping(currentDepth, options, customMapping, typeMapping, destinationMember, sourceMember); } else { // Create a deeper mapping for a 'regular' type. GenerateComplexTypeMapping(currentDepth, options, customMapping, typeMapping, destinationMember, sourceMember); } } // All we have is a destination member and a custom expression // that gives the destination member a value. Good enough! else if (customExpression != null) { typeMapping.ProposedMappings.Add ( new ProposedMemberMapping { SourceMember = null, DestinationMember = destinationMember } ); } } // Don't cache the typemapping when this flag has been set. // That happens when the maximum depth was reached during the mapping // for this particular type and we didn't explore the full depth // of the type. We don't wanna reuse this typemapping at a later time // because the mapping might be for something completely different // at a depth at which the full depth CAN be explored. if (!typeMapping.DoNotCache) { mappingCache[pair] = typeMapping; } typeStack.Pop(); return(typeMapping); }