Skip to content

Commit 8f20829

Browse files
committed
Support for passing a selector to Type.GetOperators() and Type.GetOperator()
1 parent d996c94 commit 8f20829

File tree

5 files changed

+140
-25
lines changed

5 files changed

+140
-25
lines changed

NetStandardPolyfills.UnitTests.Net40/WhenRetrievingTypeOperators.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ public class WhenRetrievingTypeOperators : OperatorTestsBase
88
[Test]
99
public override void ShouldRetrieveOperators() => DoShouldRetrieveOperators();
1010

11+
[Test]
12+
public override void ShouldRetrieveFilteredOperators() => DoShouldRetrieveFilteredOperators();
13+
1114
[Test]
1215
public override void ShouldRetrieveImplicitOperators() => DoShouldRetrieveImplicitOperators();
1316

NetStandardPolyfills.UnitTests.NetCore/WhenRetrievingTypeOperators.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ public class WhenRetrievingTypeOperators : OperatorTestsBase
77
[Fact]
88
public override void ShouldRetrieveOperators() => DoShouldRetrieveOperators();
99

10+
[Fact]
11+
public override void ShouldRetrieveFilteredOperators() => DoShouldRetrieveFilteredOperators();
12+
1013
[Fact]
1114
public override void ShouldRetrieveImplicitOperators() => DoShouldRetrieveImplicitOperators();
1215

NetStandardPolyfills.UnitTests/OperatorTestsBase.cs

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ public abstract class OperatorTestsBase
88
{
99
public abstract void ShouldRetrieveOperators();
1010

11+
public abstract void ShouldRetrieveFilteredOperators();
12+
1113
public abstract void ShouldRetrieveImplicitOperators();
1214

1315
public abstract void ShouldRetrieveImplicitOperator();
@@ -25,31 +27,68 @@ protected void DoShouldRetrieveOperators()
2527
var operators = typeof(TestHelper)
2628
.GetOperators()
2729
.OrderBy(o => o.ReturnType.Name)
30+
.ThenBy(o => o.GetParameters()[0].ParameterType.Name)
2831
.ToArray();
2932

30-
operators.Length.ShouldBe(4);
33+
operators.Length.ShouldBe(6);
3134
operators[0].ReturnType.ShouldBe(typeof(DateTime));
3235
operators[1].ReturnType.ShouldBe(typeof(int));
3336
operators[2].ReturnType.ShouldBe(typeof(int[]));
3437
operators[3].ReturnType.ShouldBe(typeof(string));
38+
operators[4].ReturnType.ShouldBe(typeof(TestHelper));
39+
operators[4].GetParameters()[0].ParameterType.ShouldBe(typeof(long));
40+
operators[5].ReturnType.ShouldBe(typeof(TestHelper));
41+
operators[5].GetParameters()[0].ParameterType.ShouldBe(typeof(string));
42+
}
43+
44+
protected void DoShouldRetrieveFilteredOperators()
45+
{
46+
var fromOperators = typeof(TestHelper)
47+
.GetOperators(o => o.From<TestHelper>())
48+
.OrderBy(o => o.ReturnType.Name)
49+
.ToArray();
50+
51+
fromOperators.Length.ShouldBe(4);
52+
fromOperators[0].ReturnType.ShouldBe(typeof(DateTime));
53+
fromOperators[0].GetParameters()[0].ParameterType.ShouldBe(typeof(TestHelper));
54+
fromOperators[1].ReturnType.ShouldBe(typeof(int));
55+
fromOperators[1].GetParameters()[0].ParameterType.ShouldBe(typeof(TestHelper));
56+
fromOperators[2].ReturnType.ShouldBe(typeof(int[]));
57+
fromOperators[2].GetParameters()[0].ParameterType.ShouldBe(typeof(TestHelper));
58+
fromOperators[3].ReturnType.ShouldBe(typeof(string));
59+
fromOperators[3].GetParameters()[0].ParameterType.ShouldBe(typeof(TestHelper));
60+
61+
var toOperators = typeof(TestHelper)
62+
.GetOperators(o => o.To<TestHelper>())
63+
.OrderBy(o => o.GetParameters()[0].ParameterType.Name)
64+
.ToArray();
65+
66+
toOperators.Length.ShouldBe(2);
67+
toOperators[0].ReturnType.ShouldBe(typeof(TestHelper));
68+
toOperators[0].GetParameters()[0].ParameterType.ShouldBe(typeof(long));
69+
toOperators[1].ReturnType.ShouldBe(typeof(TestHelper));
70+
toOperators[1].GetParameters()[0].ParameterType.ShouldBe(typeof(string));
3571
}
3672

3773
protected void DoShouldRetrieveImplicitOperators()
3874
{
3975
var operators = typeof(TestHelper)
4076
.GetImplicitOperators()
4177
.OrderBy(o => o.ReturnType.Name)
78+
.ThenBy(o => o.GetParameters()[0].ParameterType.Name)
4279
.ToArray();
4380

44-
operators.Length.ShouldBe(2);
81+
operators.Length.ShouldBe(3);
4582
operators[0].ReturnType.ShouldBe(typeof(DateTime));
4683
operators[1].ReturnType.ShouldBe(typeof(string));
84+
operators[2].ReturnType.ShouldBe(typeof(TestHelper));
85+
operators[2].GetParameters()[0].ParameterType.ShouldBe(typeof(string));
4786
}
4887

4988
protected void DoShouldRetrieveImplicitOperator()
5089
{
5190
typeof(TestHelper)
52-
.GetImplicitOperator<string>()
91+
.GetImplicitOperator(o => o.To<string>())
5392
.ShouldNotBeNull()
5493
.ReturnType.ShouldBe(typeof(string));
5594
}
@@ -59,25 +98,28 @@ protected void DoShouldRetrieveExplicitOperators()
5998
var operators = typeof(TestHelper)
6099
.GetExplicitOperators()
61100
.OrderBy(o => o.ReturnType.Name)
101+
.ThenBy(o => o.GetParameters()[0].ParameterType.Name)
62102
.ToArray();
63103

64-
operators.Length.ShouldBe(2);
104+
operators.Length.ShouldBe(3);
65105
operators[0].ReturnType.ShouldBe(typeof(int));
66106
operators[1].ReturnType.ShouldBe(typeof(int[]));
107+
operators[2].ReturnType.ShouldBe(typeof(TestHelper));
108+
operators[2].GetParameters()[0].ParameterType.ShouldBe(typeof(long));
67109
}
68110

69111
protected void DoShouldRetrieveExplicitOperator()
70112
{
71113
typeof(TestHelper)
72-
.GetExplicitOperator<int>()
114+
.GetExplicitOperator(o => o.To<int>())
73115
.ShouldNotBeNull()
74116
.ReturnType.ShouldBe(typeof(int));
75117
}
76118

77119
protected void DoShouldHandleMissingOperator()
78120
{
79121
typeof(TestHelper)
80-
.GetExplicitOperator<byte[]>()
122+
.GetExplicitOperator(o => o.From<byte[]>())
81123
.ShouldBeNull();
82124
}
83125

NetStandardPolyfills.UnitTests/TestClasses/TestHelper.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ protected TestHelper(string something)
2626

2727
public static implicit operator string(TestHelper testHelper) => testHelper.ToString();
2828
public static implicit operator DateTime(TestHelper testHelper) => DateTime.Now;
29+
public static implicit operator TestHelper(string value) => new TestHelper();
2930

3031
public static explicit operator int(TestHelper testHelper) => testHelper.GetHashCode();
3132
public static explicit operator int[] (TestHelper testHelper) => new[] { 1, 2, 3 };
33+
public static explicit operator TestHelper(long value) => new TestHelper();
3234

3335
public int PublicInstanceProperty { get; set; }
3436

NetStandardPolyfills/OperatorExtensionsPolyfill.cs

Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,29 @@ public static class OperatorExtensionsPolyfill
1414
private const string ExplicitOperatorName = "op_Explicit";
1515

1616
/// <summary>
17-
/// Gets the <paramref name="type" />'s implicit and explicit operators.
17+
/// Gets the <paramref name="type" />'s implicit and explicit operators, optionally of the type
18+
/// specified by the given <paramref name="matcher"/>.
1819
/// </summary>
1920
/// <param name="type">The type from which to retrieve the operators.</param>
21+
/// <param name="matcher">An action specifying the type of implicit operator to retrieve.</param>
2022
/// <returns>The <paramref name="type" />'s implicit and explicit operators.</returns>
21-
public static IEnumerable<MethodInfo> GetOperators(this Type type)
23+
public static IEnumerable<MethodInfo> GetOperators(this Type type, Action<OperatorSelector> matcher = null)
2224
{
23-
return type
25+
var operators = type
2426
.GetPublicStaticMembers()
2527
.Where(m => (m.Name == ImplicitOperatorName) || (m.Name == ExplicitOperatorName))
2628
.OfType<MethodInfo>();
29+
30+
if (matcher == null)
31+
{
32+
return operators;
33+
}
34+
35+
var selector = new OperatorSelector();
36+
37+
matcher.Invoke(selector);
38+
39+
return operators.Where(selector.Matches);
2740
}
2841

2942
/// <summary>
@@ -39,19 +52,22 @@ public static IEnumerable<MethodInfo> GetImplicitOperators(this Type type)
3952
}
4053

4154
/// <summary>
42-
/// Gets the <paramref name="type" />'s implicit operator with the given
43-
/// <typeparamref name="TReturn">return type</typeparamref>.
55+
/// Gets the <paramref name="type" />'s implicit operator of the type specified by the given
56+
/// <paramref name="matcher"/>.
4457
/// </summary>
45-
/// <typeparam name="TReturn">The return type of the implicit operator to retrieve.</typeparam>
4658
/// <param name="type">The type from which to retrieve the operator.</param>
59+
/// <param name="matcher">An action specifying the type of implicit operator to retrieve.</param>
4760
/// <returns>
48-
/// The <paramref name="type" />'s implicit operator with the given
49-
/// <typeparamref name="TReturn">return type</typeparamref>, or null if none exists.
61+
/// The <paramref name="type" />'s implicit operator of the type specified by the given
62+
/// <paramref name="matcher"/>, or null if none exists.
5063
/// </returns>
51-
public static MethodInfo GetImplicitOperator<TReturn>(this Type type)
64+
public static MethodInfo GetImplicitOperator(this Type type, Action<OperatorSelector> matcher)
5265
{
53-
return type.GetImplicitOperators()
54-
.FirstOrDefault(o => o.ReturnType == typeof(TReturn));
66+
var selector = new OperatorSelector();
67+
68+
matcher?.Invoke(selector);
69+
70+
return type.GetImplicitOperators().FirstOrDefault(selector.Matches);
5571
}
5672

5773
/// <summary>
@@ -67,19 +83,68 @@ public static IEnumerable<MethodInfo> GetExplicitOperators(this Type type)
6783
}
6884

6985
/// <summary>
70-
/// Gets the <paramref name="type" />'s explicit operator with the given
71-
/// <typeparamref name="TReturn">return type</typeparamref>.
86+
/// Gets the <paramref name="type" />'s explicit operator of the type specified by the given
87+
/// <paramref name="matcher"/>.
7288
/// </summary>
73-
/// <typeparam name="TReturn">The return type of the explicit operator to retrieve.</typeparam>
7489
/// <param name="type">The type from which to retrieve the operator.</param>
90+
/// <param name="matcher">An action specifying the type of explicit operator to retrieve.</param>
7591
/// <returns>
76-
/// The <paramref name="type" />'s explicit operator with the given
77-
/// <typeparamref name="TReturn">return type</typeparamref>, or null if none exists.
92+
/// The <paramref name="type" />'s explicit operator of the type specified by the given
93+
/// <paramref name="matcher"/>, or null if none exists.
7894
/// </returns>
79-
public static MethodInfo GetExplicitOperator<TReturn>(this Type type)
95+
public static MethodInfo GetExplicitOperator(this Type type, Action<OperatorSelector> matcher)
96+
{
97+
var selector = new OperatorSelector();
98+
99+
matcher?.Invoke(selector);
100+
101+
return type.GetExplicitOperators().FirstOrDefault(selector.Matches);
102+
}
103+
104+
#region Helper Class
105+
106+
/// <summary>
107+
/// Provides options for selecting a particular operator.
108+
/// </summary>
109+
public class OperatorSelector
80110
{
81-
return type.GetExplicitOperators()
82-
.FirstOrDefault(o => o.ReturnType == typeof(TReturn));
111+
private Type _fromType;
112+
private Type _toType;
113+
114+
internal OperatorSelector()
115+
{
116+
}
117+
118+
/// <summary>
119+
/// Select the operator which converts the given <typeparamref name="TInput">type</typeparamref>
120+
/// to the type in question.
121+
/// </summary>
122+
/// <typeparam name="TInput">The input type of the operator to select.</typeparam>
123+
public void From<TInput>() => _fromType = typeof(TInput);
124+
125+
/// <summary>
126+
/// Select the operator which converts the type in question to the given
127+
/// <typeparamref name="TReturn">return type</typeparamref>.
128+
/// </summary>
129+
/// <typeparam name="TReturn">The output type of the operator to select.</typeparam>
130+
public void To<TReturn>() => _toType = typeof(TReturn);
131+
132+
internal bool Matches(MethodInfo @operator)
133+
{
134+
if (_toType != null)
135+
{
136+
return @operator.ReturnType == _toType;
137+
}
138+
139+
if (_fromType != null)
140+
{
141+
return @operator.GetParameters()[0].ParameterType == _fromType;
142+
}
143+
144+
throw new InvalidOperationException("No operator From or To type specified.");
145+
}
83146
}
147+
148+
#endregion
84149
}
85150
}

0 commit comments

Comments
 (0)