Skip to content

Commit 0f87074

Browse files
committed
增加new覆盖方法的接口声明
1 parent b338422 commit 0f87074

File tree

3 files changed

+165
-5
lines changed

3 files changed

+165
-5
lines changed

WebApiClientCore.Test/HttpApiTest.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
using Xunit;
1+
using System;
2+
using Xunit;
23

34
namespace WebApiClientCore.Test
45
{
56
public class HttpApiTest
67
{
78

9+
public class NewAttribute : Attribute
10+
{
11+
12+
}
813

914
public interface IGet
1015
{
@@ -16,6 +21,12 @@ public interface IPost
1621
ITask<string> Post();
1722
}
1823

24+
public interface IPostNew : IPost
25+
{
26+
[New]
27+
new ITask<string> Post();
28+
}
29+
1930
public interface IMyApi : IGet, IPost
2031
{
2132
ITask<int> Delete();
@@ -29,6 +40,10 @@ public void GetAllApiMethodsTest()
2940

3041
Assert.False(object.ReferenceEquals(m1, m2));
3142
Assert.True(m1.Length == 3);
43+
44+
var m3 = HttpApi.FindApiMethods(typeof(IPostNew));
45+
Assert.Single(m3);
46+
Assert.True(m3[0].IsDefined(typeof(NewAttribute), true));
3247
}
3348
}
3449
}

WebApiClientCore/HttpApi.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,8 @@ public static MethodInfo[] FindApiMethods(Type httpApiType)
9797
throw new ArgumentException(Resx.required_InterfaceType.Format(httpApiType.Name));
9898
}
9999

100-
return httpApiType
101-
.GetInterfaces()
102-
.Append(httpApiType)
103-
.SelectMany(item => item.GetMethods())
100+
return HttpApiMethodFinder
101+
.FindApiMethods(httpApiType)
104102
.Select(item => item.EnsureApiMethod())
105103
.ToArray();
106104
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
6+
namespace WebApiClientCore
7+
{
8+
/// <summary>
9+
/// HttpApi的方法查找器
10+
/// </summary>
11+
static class HttpApiMethodFinder
12+
{
13+
/// <summary>
14+
/// 查找接口类型及其继承的接口的所有方法
15+
/// </summary>
16+
/// <param name="httpApiType">接口类型</param>
17+
/// <exception cref="ArgumentException"></exception>
18+
/// <exception cref="NotSupportedException"></exception>
19+
/// <returns></returns>
20+
public static IEnumerable<MethodInfo> FindApiMethods(Type httpApiType)
21+
{
22+
var interfaces = httpApiType.GetInterfaces().Append(httpApiType);
23+
return Sort(interfaces, t => t.GetInterfaces())
24+
.Reverse()
25+
.SelectMany(item => item.GetMethods().Select(m => new MethodFeature(m)))
26+
.Distinct()
27+
.Select(item => item.Method);
28+
}
29+
30+
/// <summary>
31+
/// https://www.cnblogs.com/myzony/p/9201768.html
32+
/// </summary>
33+
/// <typeparam name="T"></typeparam>
34+
/// <param name="source"></param>
35+
/// <param name="getDependencies"></param>
36+
/// <returns></returns>
37+
private static IList<T> Sort<T>(IEnumerable<T> source, Func<T, IEnumerable<T>> getDependencies)
38+
{
39+
var sorted = new List<T>();
40+
var visited = new Dictionary<T, bool>();
41+
42+
foreach (var item in source)
43+
{
44+
Visit(item, getDependencies, sorted, visited);
45+
}
46+
47+
return sorted;
48+
}
49+
50+
private static void Visit<T>(T item, Func<T, IEnumerable<T>> getDependencies, List<T> sorted, Dictionary<T, bool> visited)
51+
{
52+
bool inProcess;
53+
var alreadyVisited = visited.TryGetValue(item, out inProcess);
54+
55+
// 如果已经访问该顶点,则直接返回
56+
if (alreadyVisited)
57+
{
58+
// 如果处理的为当前节点,则说明存在循环引用
59+
if (inProcess)
60+
{
61+
throw new ArgumentException("Cyclic dependency found.");
62+
}
63+
}
64+
else
65+
{
66+
// 正在处理当前顶点
67+
visited[item] = true;
68+
69+
// 获得所有依赖项
70+
var dependencies = getDependencies(item);
71+
// 如果依赖项集合不为空,遍历访问其依赖节点
72+
if (dependencies != null)
73+
{
74+
foreach (var dependency in dependencies)
75+
{
76+
// 递归遍历访问
77+
Visit(dependency, getDependencies, sorted, visited);
78+
}
79+
}
80+
81+
// 处理完成置为 false
82+
visited[item] = false;
83+
sorted.Add(item);
84+
}
85+
}
86+
87+
/// <summary>
88+
/// 表示MethodInfo的特征
89+
/// </summary>
90+
private class MethodFeature : IEquatable<MethodFeature>
91+
{
92+
private readonly MethodInfo method;
93+
94+
public MethodInfo Method => method;
95+
96+
/// <summary>
97+
/// MethodInfo的特征
98+
/// </summary>
99+
/// <param name="method"></param>
100+
public MethodFeature(MethodInfo method)
101+
{
102+
this.method = method;
103+
}
104+
105+
/// <summary>
106+
/// 比较方法原型是否相等
107+
/// </summary>
108+
/// <param name="other"></param>
109+
/// <returns></returns>
110+
public bool Equals(MethodFeature other)
111+
{
112+
var x = this.method;
113+
var y = other.method;
114+
115+
if (x.Name != y.Name || x.ReturnType != y.ReturnType)
116+
{
117+
return false;
118+
}
119+
120+
var xParameterTypes = x.GetParameters().Select(p => p.ParameterType);
121+
var yParameterTypes = y.GetParameters().Select(p => p.ParameterType);
122+
return xParameterTypes.SequenceEqual(yParameterTypes);
123+
}
124+
125+
/// <summary>
126+
/// 获取哈希
127+
/// </summary>
128+
/// <returns></returns>
129+
public override int GetHashCode()
130+
{
131+
var hashCode = new HashCode();
132+
hashCode.Add(this.method.Name);
133+
hashCode.Add(this.method.ReturnType);
134+
foreach (var parameter in this.method.GetParameters())
135+
{
136+
hashCode.Add(parameter.ParameterType);
137+
}
138+
return hashCode.ToHashCode();
139+
}
140+
141+
public override bool Equals(object obj)
142+
{
143+
return obj is MethodFeature other && this.Equals(other);
144+
}
145+
}
146+
}
147+
}

0 commit comments

Comments
 (0)