1
+ // --------------------------------------------------------------------------------------------------------------------
2
+ // <copyright file="TypeInferenceExtensions.cs" company="James South">
3
+ // Copyright (c) James South.
4
+ // Licensed under the Apache License, Version 2.0.
5
+ // </copyright>
6
+ // <summary>
7
+ // Extensions methods for <see cref="T:System.Type" /> for inferring type properties.
8
+ // Most of this code was adapted from the Entity Framework
9
+ // </summary>
10
+ // --------------------------------------------------------------------------------------------------------------------
11
+
12
+ namespace ImageProcessor . Web . Extensions
13
+ {
14
+ using System ;
15
+ using System . Collections . Generic ;
16
+ using System . Linq ;
17
+
18
+ /// <summary>
19
+ /// Extensions methods for <see cref="T:System.Type"/> for inferring type properties.
20
+ /// Most of this code was adapted from the Entity Framework
21
+ /// </summary>
22
+ internal static class TypeInferenceExtensions
23
+ {
24
+ /// <summary>
25
+ /// Determines whether the specified type is an enumerable of the given argument type.
26
+ /// </summary>
27
+ /// <param name="type">The type.</param>
28
+ /// <param name="typeArgument">The generic type argument.</param>
29
+ /// <returns>
30
+ /// True if the type is an enumerable of the given argument type otherwise; false.
31
+ /// </returns>
32
+ public static bool IsEnumerableOfType ( this Type type , Type typeArgument )
33
+ {
34
+ Type t = type . TryGetElementType ( typeof ( IEnumerable < > ) ) ;
35
+ return t != null && typeArgument . IsAssignableFrom ( t ) ;
36
+ }
37
+
38
+ /// <summary>
39
+ /// Determines whether the specified type is a collection type.
40
+ /// </summary>
41
+ /// <param name="type">The type.</param>
42
+ /// <returns>True if the type is a collection type otherwise; false.</returns>
43
+ public static bool IsCollectionType ( this Type type )
44
+ {
45
+ return type . TryGetElementType ( typeof ( ICollection < > ) ) != null ;
46
+ }
47
+
48
+ /// <summary>
49
+ /// Determines whether the specified type is an enumerable type.
50
+ /// </summary>
51
+ /// <param name="type">The type.</param>
52
+ /// <returns>True if the type is an enumerable type otherwise; false.</returns>
53
+ public static bool IsEnumerableType ( this Type type )
54
+ {
55
+ return type . TryGetElementType ( typeof ( IEnumerable < > ) ) != null ;
56
+ }
57
+
58
+ /// <summary>
59
+ /// Determines whether the specified type is an enumerable type containing a
60
+ /// key value pair as the generic type parameter.
61
+ /// <remarks>
62
+ /// <see cref="M:Enumerable.FirstOrDefault"/> will throw an error when passed an
63
+ /// <see cref="T:IEnumerable{KeyValuePair{,}}"/> this includes <see cref="T:Dictionary{,}"/>.
64
+ /// </remarks>
65
+ /// </summary>
66
+ /// <param name="type">The type.</param>
67
+ /// <returns>
68
+ /// True if the type is an enumerable type with the generic parameter of a key/value
69
+ /// pair otherwise; false.</returns>
70
+ public static bool IsEnumerableOfKeyValueType ( this Type type )
71
+ {
72
+ return type . TryGetElementType ( typeof ( IDictionary < , > ) ) != null ||
73
+ ( type . IsEnumerableType ( ) && type . IsGenericType && type . GenericTypeArguments . Any ( )
74
+ && type . GenericTypeArguments [ 0 ] . IsGenericType
75
+ && type . GenericTypeArguments [ 0 ] . GetGenericTypeDefinition ( ) == typeof ( KeyValuePair < , > ) ) ;
76
+ }
77
+
78
+ /// <summary>
79
+ /// Determines whether the specified type is an enumerable type that is safe to cast
80
+ /// following processing via a type converter.
81
+ /// <remarks>
82
+ /// This should exclude <see cref="T:string"/>, <see cref="T:Dictionary{,}"/>
83
+ /// </remarks>
84
+ /// </summary>
85
+ /// <param name="type">The type.</param>
86
+ /// <returns>True if the type is a cast-safe, enumerable type otherwise; false.</returns>
87
+ public static bool IsCastableEnumerableType ( this Type type )
88
+ {
89
+ // String, though enumerable have no generic arguments.
90
+ // Types with more than one generic argument cnnot be cast.
91
+ // Dictionary, though enumerable, requires linq to convert and shouldn't be attempted anyway.
92
+ return type . IsEnumerableType ( ) && type . GenericTypeArguments . Any ( )
93
+ && type . GenericTypeArguments . Length == 1
94
+ && type . TryGetElementType ( typeof ( IDictionary < , > ) ) == null ;
95
+ }
96
+
97
+ /// <summary>
98
+ /// Determine if the given type implements the given generic interface or derives from the given generic type,
99
+ /// and if so return the element type of the collection. If the type implements the generic interface several times
100
+ /// <c>null</c> will be returned.
101
+ /// </summary>
102
+ /// <param name="type">The type to examine. </param>
103
+ /// <param name="interfaceOrBaseType"> The generic type to be queried for. </param>
104
+ /// <returns>
105
+ /// <c>null</c> if <paramref name="interfaceOrBaseType"/> isn't implemented or implemented multiple times,
106
+ /// otherwise the generic argument.
107
+ /// </returns>
108
+ public static Type TryGetElementType ( this Type type , Type interfaceOrBaseType )
109
+ {
110
+ if ( ! type . IsGenericTypeDefinition )
111
+ {
112
+ Type [ ] types = GetGenericTypeImplementations ( type , interfaceOrBaseType ) . ToArray ( ) ;
113
+ return types . Length == 1 ? types [ 0 ] . GetGenericArguments ( ) . FirstOrDefault ( ) : null ;
114
+ }
115
+
116
+ return null ;
117
+ }
118
+
119
+ /// <summary>
120
+ /// Determine if the given type implements the given generic interface or derives from the given generic type,
121
+ /// and if so return the concrete types implemented.
122
+ /// </summary>
123
+ /// <param name="type"> The type to examine. </param>
124
+ /// <param name="interfaceOrBaseType"> The generic type to be queried for. </param>
125
+ /// <returns>
126
+ /// The generic types constructed from <paramref name="interfaceOrBaseType"/> and implemented by <paramref name="type"/>.
127
+ /// </returns>
128
+ public static IEnumerable < Type > GetGenericTypeImplementations ( this Type type , Type interfaceOrBaseType )
129
+ {
130
+ if ( ! type . IsGenericTypeDefinition )
131
+ {
132
+ return ( interfaceOrBaseType . IsInterface ? type . GetInterfaces ( ) : type . GetBaseTypes ( ) )
133
+ . Union ( new [ ] { type } )
134
+ . Where ( t => t . IsGenericType && t . GetGenericTypeDefinition ( ) == interfaceOrBaseType ) ;
135
+ }
136
+
137
+ return Enumerable . Empty < Type > ( ) ;
138
+ }
139
+
140
+ /// <summary>
141
+ /// Gets the base types that the given type inherits from
142
+ /// </summary>
143
+ /// <param name="type">The <see cref="Type"/> to get the base types from.</param>
144
+ /// <returns>A collection of base types that the given type inherits from.</returns>
145
+ public static IEnumerable < Type > GetBaseTypes ( this Type type )
146
+ {
147
+ type = type . BaseType ;
148
+
149
+ while ( type != null )
150
+ {
151
+ yield return type ;
152
+
153
+ type = type . BaseType ;
154
+ }
155
+ }
156
+
157
+ /// <summary>
158
+ /// Gets the type of the enumerable object
159
+ /// </summary>
160
+ /// <param name="type">The <see cref="Type"/> to check.</param>
161
+ /// <returns>The type of the enumerable.</returns>
162
+ public static Type GetEnumerableType ( this Type type )
163
+ {
164
+ List < Type > interfaces = type . GetInterfaces ( ) . ToList ( ) ;
165
+ if ( type . IsInterface && interfaces . All ( i => i != type ) )
166
+ {
167
+ interfaces . Add ( type ) ;
168
+ }
169
+
170
+ return interfaces
171
+ . Where ( i => i . IsGenericType && i . GetGenericTypeDefinition ( ) == typeof ( IEnumerable < > ) )
172
+ . Select ( i => i . GetGenericArguments ( ) [ 0 ] ) . FirstOrDefault ( ) ;
173
+ }
174
+ }
175
+ }
0 commit comments