/
Extensions.cs
187 lines (160 loc) 路 7.52 KB
/
Extensions.cs
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
锘縰sing System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Xpand.Utils.Linq{
public static class Extensions{
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int takeCount){
if (source == null) throw new ArgumentNullException(nameof(source));
if (takeCount < 0) throw new ArgumentOutOfRangeException(nameof(takeCount), "must not be negative");
if (takeCount == 0) yield break;
var result = new T[takeCount];
var i = 0;
var sourceCount = 0;
foreach (var element in source){
result[i] = element;
i = (i + 1) % takeCount;
sourceCount++;
}
if (sourceCount < takeCount){
takeCount = sourceCount;
i = 0;
}
for (var j = 0; j < takeCount; ++j) yield return result[(i + j) % takeCount];
}
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts) {
int i = 0;
return list.GroupBy(item => i++%parts).Select(part => part.AsEnumerable());
}
public static IEnumerable<T> SkipLastN<T>(this IEnumerable<T> source, int n) {
var it = source.GetEnumerator();
bool hasRemainingItems;
var cache = new Queue<T>(n + 1);
do{
var b = hasRemainingItems = it.MoveNext();
if (b) {
cache.Enqueue(it.Current);
if (cache.Count > n)
yield return cache.Dequeue();
}
} while (hasRemainingItems);
}
public static IEnumerable<TResult> SelectNonNull<T, TResult>(this IEnumerable<T> sequence,
Func<T, TResult> projection) where TResult : class{
return sequence.Select(projection).Where(e => e != null);
}
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T> sequence) where T : class{
return sequence.Where(e => e != null);
}
public static TResult NullSafeEval<TSource, TResult>(this TSource source,
Expression<Func<TSource, TResult>> expression, TResult defaultValue){
Expression<Func<TSource, TResult>> safeExp = Expression.Lambda<Func<TSource, TResult>>(
NullSafeEvalWrapper(expression.Body, Expression.Constant(defaultValue)),
expression.Parameters[0]);
Func<TSource, TResult> safeDelegate = safeExp.Compile();
return safeDelegate(source);
}
private static Expression NullSafeEvalWrapper(Expression expr, Expression defaultValue){
Expression obj;
Expression safe = expr;
while (!IsNullSafe(expr, out obj)){
BinaryExpression isNull = Expression.Equal(obj, Expression.Constant(null));
safe =
Expression.Condition
(
isNull,
defaultValue,
safe
);
expr = obj;
}
return safe;
}
private static bool IsNullSafe(Expression expr, out Expression nullableObject){
nullableObject = null;
if (expr is MemberExpression || expr is MethodCallExpression){
Expression obj;
var memberExpr = expr as MemberExpression;
var callExpr = expr as MethodCallExpression;
if (memberExpr != null){
// Static fields don't require an instance
var field = memberExpr.Member as FieldInfo;
if (field != null && field.IsStatic)
return true;
// Static properties don't require an instance
var property = memberExpr.Member as PropertyInfo;
MethodInfo getter = property?.GetGetMethod();
if (getter != null && getter.IsStatic)
return true;
obj = memberExpr.Expression;
}
else{
// Static methods don't require an instance
if (callExpr.Method.IsStatic)
return true;
obj = callExpr.Object;
}
// Value types can't be null
if (obj != null && obj.Type.IsValueType)
return true;
// Instance member access or instance method call is not safe
nullableObject = obj;
return false;
}
return true;
}
public static void ThrowIfNull<T>(this T container) where T : class {
if (container == null) {
throw new ArgumentNullException(nameof(container));
}
NullChecker<T>.Check(container);
}
private static class NullChecker<T> where T : class {
private static readonly List<Func<T, bool>> _checkers;
private static readonly List<string> _names;
static NullChecker() {
_checkers = new List<Func<T, bool>>();
_names = new List<string>();
// We can't rely on the order of the properties, but we
// can rely on the order of the constructor parameters
// in an anonymous type - and that there'll only be
// one constructor.
var constructorInfo = typeof(T).GetConstructors().FirstOrDefault();
if (constructorInfo != null)
foreach (string name in constructorInfo.GetParameters().Select(p => p.Name)) {
_names.Add(name);
PropertyInfo property = typeof(T).GetProperty(name);
// I've omitted a lot of error checking, but here's
// at least one bit...
if (property.PropertyType.IsValueType) {
throw new ArgumentException
("Property " + property + " is a value type");
}
ParameterExpression param = Expression.Parameter(typeof(T), "container");
Expression propertyAccess = Expression.Property(param, property);
Expression nullValue = Expression.Constant(null, property.PropertyType);
Expression equality = Expression.Equal(propertyAccess, nullValue);
var lambda = Expression.Lambda<Func<T, bool>>(equality, param);
_checkers.Add(lambda.Compile());
}
}
internal static void Check(T item) {
for (int i = 0; i < _checkers.Count; i++) {
if (_checkers[i](item)) {
throw new ArgumentNullException(_names[i]);
}
}
}
}
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> sequence)
where T : struct{
return sequence.Where(e => e != null).Select(e => e.Value);
}
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector){
var seenKeys = new HashSet<TKey>();
return source.Where(element => seenKeys.Add(keySelector(element)));
}
}
}