-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDataType.cs
More file actions
135 lines (109 loc) · 4.26 KB
/
Copy pathDataType.cs
File metadata and controls
135 lines (109 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace DataKit.Modelling
{
/// <summary>
/// Describes a data type.
/// </summary>
public abstract class DataType
{
/// <summary>
/// Gets the data type.
/// </summary>
public abstract Type Type { get; }
public abstract bool IsEnumerable { get; }
public abstract Type ElementType { get; }
/// <summary>
/// Gets a value indicating if the enumerable type is pure.
/// </summary>
/// <remarks>
/// This is helpful for catching types like <see cref="String">string</see> which are technically an enumerable type but are more useful as their usual counterpart.
/// </remarks>
public abstract bool IsPureEnumerable { get; }
/// <summary>
/// Gets a value indicating if the type is a compiler generated anonymous type.
/// </summary>
public abstract bool IsAnonymousType { get; }
private static readonly CachedCollection<Type, Type, DataType> _instanceCache
= new CachedCollection<Type, Type, DataType>(CreateNew);
public static DataType GetTypeOf(Type type)
{
return _instanceCache.GetOrCreate(type, type);
}
public static DataType<T> GetTypeOf<T>()
=> GetTypeOf(typeof(T)) as DataType<T>;
private static DataType CreateNew(Type type)
{
var ctor = typeof(DataType<>).MakeGenericType(type)
.GetConstructors(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
.First();
return ctor.Invoke(null) as DataType;
}
}
/// <summary>
/// Describes a data type of type T.
/// </summary>
/// <typeparam name="T"></typeparam>
public class DataType<T> : DataType
{
public override Type Type => typeof(T);
public override bool IsEnumerable { get; }
public override Type ElementType { get; }
public override bool IsPureEnumerable { get; }
public override bool IsAnonymousType { get; }
private DataType()
{
ElementType = GetEnumerableElementType();
IsEnumerable = ElementType != null;
IsPureEnumerable = IsEnumerable && IsPureEnumerableType(ElementType);
IsAnonymousType = DetectIsAnonymousType();
}
private static Type _enumerableCompareType = typeof(IEnumerable<>);
private static bool DetectIsAnonymousType()
{
var checkType = typeof(T);
if (checkType.IsClass && checkType.IsSealed && checkType.IsNotPublic && checkType.BaseType == typeof(object)
&& checkType.GetCustomAttributes(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false) != null)
return true;
return false;
}
private static bool IsPureEnumerableType(Type elementType)
{
var checkType = typeof(T);
var collectionTypes = new[]
{
typeof(ICollection),
typeof(ICollection<>).MakeGenericType(elementType),
typeof(IReadOnlyCollection<>).MakeGenericType(elementType)
};
// checkType is not an implementation of IEnumerable but IEnumerable/ICollection directly, it's pure
if (checkType == typeof(IEnumerable) || collectionTypes.Contains(checkType))
return true;
// checkType is an array, it's pure
if (checkType.IsArray)
return true;
var checkInterfaces = checkType.GetInterfaces();
// collection types are considered pure, this takes care of just about all cases
if (checkInterfaces.Any(iface => collectionTypes.Contains(iface)))
return true;
// checkType isn't generic, so can't possibly be IEnumerable<elementType>, it fails purity
if (!checkType.IsGenericType)
return false;
return checkType == typeof(IEnumerable<>).MakeGenericType(elementType);
}
private static Type GetEnumerableElementType()
{
var type = typeof(T);
var interfaces = type.GetInterfaces();
var enumerableInterfaceType = interfaces
.FirstOrDefault(q => q.IsGenericType && q.GetGenericTypeDefinition() == _enumerableCompareType);
if (enumerableInterfaceType == null && type.IsGenericType && type.GetGenericTypeDefinition() == _enumerableCompareType)
enumerableInterfaceType = type;
if (enumerableInterfaceType == null)
return type == typeof(IEnumerable) || interfaces.Contains(typeof(IEnumerable)) ? typeof(object) : null;
return enumerableInterfaceType.GetGenericArguments()[0];
}
}
}