diff --git a/WebApiClientCore.Analyzers/SourceGenerator/HttpApiProxyClass.cs b/WebApiClientCore.Analyzers/SourceGenerator/HttpApiProxyClass.cs index d10c7292..876c2289 100644 --- a/WebApiClientCore.Analyzers/SourceGenerator/HttpApiProxyClass.cs +++ b/WebApiClientCore.Analyzers/SourceGenerator/HttpApiProxyClass.cs @@ -103,13 +103,17 @@ public override string ToString() builder.AppendLine(); var index = 0; - foreach (var method in this.FindApiMethods()) + foreach (var interfaceType in this.httpApi.AllInterfaces.Append(httpApi)) { - var methodCode = this.BuildMethod(method, index); - builder.AppendLine(methodCode); - index += 1; + foreach (var method in interfaceType.GetMembers().OfType()) + { + var methodCode = this.BuildMethod(interfaceType, method, index); + builder.AppendLine(methodCode); + index += 1; + } } + builder.AppendLine("\t}"); builder.AppendLine("}"); builder.AppendLine("#pragma warning restore"); @@ -117,27 +121,14 @@ public override string ToString() return builder.ToString(); } - /// - /// 查找接口类型及其继承的接口的所有方法 - /// - /// - private IEnumerable FindApiMethods() - { -#pragma warning disable RS1024 - return this.httpApi.AllInterfaces.Append(httpApi) - .SelectMany(item => item.GetMembers()) - .OfType() - .Distinct(MethodEqualityComparer.Default); -#pragma warning restore RS1024 - } - /// /// 构建方法 /// + /// /// /// /// - private string BuildMethod(IMethodSymbol method, int index) + private string BuildMethod(INamedTypeSymbol interfaceType, IMethodSymbol method, int index) { var builder = new StringBuilder(); var parametersString = string.Join(",", method.Parameters.Select(item => $"{GetFullName(item.Type)} {item.Name}")); @@ -146,9 +137,10 @@ private string BuildMethod(IMethodSymbol method, int index) ? "global::System.Array.Empty()" : $"new global::System.Object[] {{ {parameterNamesString} }}"; + var methodName = $"\"{interfaceType.ToDisplayString()}.{method.Name}\""; var returnTypeString = GetFullName(method.ReturnType); - builder.AppendLine($"\t\t[global::WebApiClientCore.HttpApiProxyMethod({index})]"); - builder.AppendLine($"\t\tpublic {returnTypeString} {method.Name}( {parametersString} )"); + builder.AppendLine($"\t\t[global::WebApiClientCore.HttpApiProxyMethod({index}, {methodName})]"); + builder.AppendLine($"\t\t{returnTypeString} {GetFullName(interfaceType)}.{method.Name}( {parametersString} )"); builder.AppendLine("\t\t{"); builder.AppendLine($"\t\t\treturn ({returnTypeString})this.{this.apiInterceptorFieldName}.Intercept(this.{this.actionInvokersFieldName}[{index}], {paremterArrayString});"); builder.AppendLine("\t\t}"); diff --git a/WebApiClientCore.Test/HttpApiTest.cs b/WebApiClientCore.Test/HttpApiTest.cs index bace6e32..895a2cda 100644 --- a/WebApiClientCore.Test/HttpApiTest.cs +++ b/WebApiClientCore.Test/HttpApiTest.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Xunit; namespace WebApiClientCore.Test @@ -39,11 +40,11 @@ public void GetAllApiMethodsTest() var m2 = HttpApi.FindApiMethods(typeof(IMyApi)); Assert.False(object.ReferenceEquals(m1, m2)); - Assert.True(m1.Length == 3); + Assert.Equal(3, m1.Length); var m3 = HttpApi.FindApiMethods(typeof(IPostNew)); - Assert.Single(m3); - Assert.True(m3[0].IsDefined(typeof(NewAttribute), true)); + Assert.Equal(2, m3.Length); + Assert.Contains(m3, i => i.IsDefined(typeof(NewAttribute), true)); } } } diff --git a/WebApiClientCore/Exceptions/ProxyTypeCreateException.cs b/WebApiClientCore/Exceptions/ProxyTypeCreateException.cs index 37d2d35b..32e52b5c 100644 --- a/WebApiClientCore/Exceptions/ProxyTypeCreateException.cs +++ b/WebApiClientCore/Exceptions/ProxyTypeCreateException.cs @@ -5,19 +5,14 @@ namespace WebApiClientCore.Exceptions /// /// 表示代理类创建异常 /// - public class ProxyTypeCreateException : Exception + public class ProxyTypeCreateException : ProxyTypeException { - /// - /// 接口类型 - /// - public Type InterfaceType { get; } - /// /// 代理类创建异常 /// /// 接口类型 public ProxyTypeCreateException(Type interfaceType) - : this(interfaceType, null) + : base(interfaceType) { } @@ -27,9 +22,8 @@ public ProxyTypeCreateException(Type interfaceType) /// 接口类型 /// 提示消息 public ProxyTypeCreateException(Type interfaceType, string? message) - : base(message) + : base(interfaceType, message) { - this.InterfaceType = interfaceType; } } } diff --git a/WebApiClientCore/Exceptions/ProxyTypeException.cs b/WebApiClientCore/Exceptions/ProxyTypeException.cs new file mode 100644 index 00000000..045f868f --- /dev/null +++ b/WebApiClientCore/Exceptions/ProxyTypeException.cs @@ -0,0 +1,35 @@ +using System; + +namespace WebApiClientCore.Exceptions +{ + /// + /// 表示代理类异常 + /// + public class ProxyTypeException : Exception + { + /// + /// 接口类型 + /// + public Type InterfaceType { get; } + + /// + /// 代理类异常 + /// + /// 接口类型 + public ProxyTypeException(Type interfaceType) + : this(interfaceType, null) + { + } + + /// + /// 代理类创建异常 + /// + /// 接口类型 + /// 提示消息 + public ProxyTypeException(Type interfaceType, string? message) + : base(message) + { + this.InterfaceType = interfaceType; + } + } +} diff --git a/WebApiClientCore/HttpApi.cs b/WebApiClientCore/HttpApi.cs index 14ad9e71..a680e252 100644 --- a/WebApiClientCore/HttpApi.cs +++ b/WebApiClientCore/HttpApi.cs @@ -97,8 +97,8 @@ public static MethodInfo[] FindApiMethods(Type httpApiType) throw new ArgumentException(Resx.required_InterfaceType.Format(httpApiType.Name)); } - return HttpApiMethodFinder - .FindApiMethods(httpApiType) + return httpApiType.GetInterfaces().Append(httpApiType) + .SelectMany(item => item.GetMethods()) .Select(item => item.EnsureApiMethod()) .ToArray(); } diff --git a/WebApiClientCore/HttpApiMethodFinder.cs b/WebApiClientCore/HttpApiMethodFinder.cs deleted file mode 100644 index db9e2bce..00000000 --- a/WebApiClientCore/HttpApiMethodFinder.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace WebApiClientCore -{ - /// - /// HttpApi的方法查找器 - /// - static class HttpApiMethodFinder - { - /// - /// 查找接口类型及其继承的接口的所有方法 - /// - /// 接口类型 - /// - /// - public static IEnumerable FindApiMethods(Type httpApiType) - { - var interfaces = httpApiType.GetInterfaces().Append(httpApiType); - return Sort(interfaces, t => t.GetInterfaces()) - .Reverse() - .SelectMany(item => item.GetMethods()) - .Distinct(MethodEqualityComparer.Default); - } - - /// - /// https://www.cnblogs.com/myzony/p/9201768.html - /// - /// - /// - /// - /// - private static IList Sort(IEnumerable source, Func> getDependencies) where T : notnull - { - var sorted = new List(); - var visited = new Dictionary(); - - foreach (var item in source) - { - Visit(item, getDependencies, sorted, visited); - } - - return sorted; - } - - private static void Visit(T item, Func> getDependencies, List sorted, Dictionary visited) where T : notnull - { - var alreadyVisited = visited.TryGetValue(item, out var inProcess); - - // 如果已经访问该顶点,则直接返回 - if (alreadyVisited) - { - // 如果处理的为当前节点,则说明存在循环引用 - if (inProcess) - { - throw new ArgumentException("Cyclic dependency found."); - } - } - else - { - // 正在处理当前顶点 - visited[item] = true; - - // 获得所有依赖项 - var dependencies = getDependencies(item); - // 如果依赖项集合不为空,遍历访问其依赖节点 - if (dependencies != null) - { - foreach (var dependency in dependencies) - { - // 递归遍历访问 - Visit(dependency, getDependencies, sorted, visited); - } - } - - // 处理完成置为 false - visited[item] = false; - sorted.Add(item); - } - } - - /// - /// 表示MethodInfo的相等比较器 - /// - private class MethodEqualityComparer : IEqualityComparer - { - public static MethodEqualityComparer Default { get; } = new MethodEqualityComparer(); - - public bool Equals(MethodInfo? x, MethodInfo? y) - { - if (x == null || y == null) - { - return false; - } - - if (x.Name != y.Name || x.ReturnType != y.ReturnType) - { - return false; - } - - var xParameterTypes = x.GetParameters().Select(p => p.ParameterType); - var yParameterTypes = y.GetParameters().Select(p => p.ParameterType); - return xParameterTypes.SequenceEqual(yParameterTypes); - } - - public int GetHashCode(MethodInfo obj) - { - var hashCode = new HashCode(); - hashCode.Add(obj.Name); - hashCode.Add(obj.ReturnType); - foreach (var parameter in obj.GetParameters()) - { - hashCode.Add(parameter.ParameterType); - } - return hashCode.ToHashCode(); - } - } - } -} diff --git a/WebApiClientCore/HttpApiProxyMethodAttribute.cs b/WebApiClientCore/HttpApiProxyMethodAttribute.cs index 2ffac574..17287bfc 100644 --- a/WebApiClientCore/HttpApiProxyMethodAttribute.cs +++ b/WebApiClientCore/HttpApiProxyMethodAttribute.cs @@ -15,13 +15,20 @@ public sealed class HttpApiProxyMethodAttribute : Attribute /// public int Index { get; } + /// + /// 获取名称 + /// + public string Name { get; } + /// /// 方法的索引特性 /// /// 索引值,确保连续且不重复 - public HttpApiProxyMethodAttribute(int index) + /// 方法的名称 + public HttpApiProxyMethodAttribute(int index, string name) { this.Index = index; + this.Name = name; } } } diff --git a/WebApiClientCore/Implementations/EmitHttpApiActivator.cs b/WebApiClientCore/Implementations/EmitHttpApiActivator.cs index 2005c7e4..622cfc9a 100644 --- a/WebApiClientCore/Implementations/EmitHttpApiActivator.cs +++ b/WebApiClientCore/Implementations/EmitHttpApiActivator.cs @@ -15,7 +15,7 @@ public class EmitHttpApiActivator< #if NET5_0_OR_GREATER [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] #endif - THttpApi> : IHttpApiActivator + THttpApi> : IHttpApiActivator { private readonly ApiActionInvoker[] actionInvokers; private readonly Func activator; @@ -33,7 +33,7 @@ public EmitHttpApiActivator(IApiActionDescriptorProvider apiActionDescriptorProv this.actionInvokers = apiMethods .Select(item => apiActionDescriptorProvider.CreateActionDescriptor(item, typeof(THttpApi))) - .Select(item => actionInvokerProvider.CreateActionInvoker(item)) + .Select(actionInvokerProvider.CreateActionInvoker) .ToArray(); var proxyType = BuildProxyType(typeof(THttpApi), apiMethods); @@ -151,17 +151,19 @@ private static void BuildCtor(TypeBuilder builder, FieldBuilder fieldApiIntercep /// action执行器字段 private static void BuildMethods(TypeBuilder builder, MethodInfo[] actionMethods, FieldBuilder fieldApiInterceptor, FieldBuilder fieldActionInvokers) { - const MethodAttributes implementAttribute = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot | MethodAttributes.HideBySig; + // private final hidebysig newslot virtual + const MethodAttributes implementAttribute = MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; for (var i = 0; i < actionMethods.Length; i++) { var actionMethod = actionMethods[i]; var actionParameters = actionMethod.GetParameters(); var parameterTypes = actionParameters.Select(p => p.ParameterType).ToArray(); + var actionMethodName = $"{actionMethod.DeclaringType?.FullName}.{actionMethod.Name}"; - var iL = builder - .DefineMethod(actionMethod.Name, implementAttribute, CallingConventions.Standard, actionMethod.ReturnType, parameterTypes) - .GetILGenerator(); + var methodBuilder = builder.DefineMethod(actionMethodName, implementAttribute, CallingConventions.Standard | CallingConventions.HasThis, actionMethod.ReturnType, parameterTypes); + builder.DefineMethodOverride(methodBuilder, actionMethod); + var iL = methodBuilder.GetILGenerator(); // this.apiInterceptor iL.Emit(OpCodes.Ldarg_0); diff --git a/WebApiClientCore/Implementations/SourceGeneratorHttpApiActivator.cs b/WebApiClientCore/Implementations/SourceGeneratorHttpApiActivator.cs index 6f4f68b8..1901dd70 100644 --- a/WebApiClientCore/Implementations/SourceGeneratorHttpApiActivator.cs +++ b/WebApiClientCore/Implementations/SourceGeneratorHttpApiActivator.cs @@ -19,12 +19,12 @@ public class SourceGeneratorHttpApiActivator< { private readonly ApiActionInvoker[] actionInvokers; private readonly Func activator; - private static readonly Type? proxyClassType = FindProxyTypeFromAssembly(); + private static readonly Type? _proxyClassType = SourceGeneratorProxyClassType.Find(typeof(THttpApi)); /// /// 获取是否支持 /// - public static bool IsSupported => proxyClassType != null; + public static bool IsSupported => _proxyClassType != null; /// /// 通过查找类型代理类型创建实例 @@ -36,19 +36,20 @@ public class SourceGeneratorHttpApiActivator< /// public SourceGeneratorHttpApiActivator(IApiActionDescriptorProvider apiActionDescriptorProvider, IApiActionInvokerProvider actionInvokerProvider) { - var proxyType = proxyClassType; - if (proxyType == null) + var httpApiType = typeof(THttpApi); + var proxyClassType = _proxyClassType; + if (proxyClassType == null) { - var message = $"找不到{typeof(THttpApi)}的代理类"; - throw new ProxyTypeCreateException(typeof(THttpApi), message); + var message = $"找不到{httpApiType}的代理类"; + throw new ProxyTypeCreateException(httpApiType, message); } - this.actionInvokers = FindApiMethods(proxyType) - .Select(item => apiActionDescriptorProvider.CreateActionDescriptor(item, typeof(THttpApi))) + this.actionInvokers = FindApiMethods(httpApiType, proxyClassType) + .Select(item => apiActionDescriptorProvider.CreateActionDescriptor(item, httpApiType)) .Select(actionInvokerProvider.CreateActionInvoker) .ToArray(); - this.activator = LambdaUtil.CreateCtorFunc(proxyType); + this.activator = LambdaUtil.CreateCtorFunc(proxyClassType); } /// @@ -62,49 +63,31 @@ public THttpApi CreateInstance(IHttpApiInterceptor apiInterceptor) } /// - /// 查找接口的Api方法 + /// 查找接口的方法 /// + /// 接口类型 + /// 接口的实现类型 /// - private static MethodInfo[] FindApiMethods(Type proxyType) + private static MethodInfo[] FindApiMethods(Type httpApiType, Type proxyClassType) { - var apiMethods = HttpApi.FindApiMethods(typeof(THttpApi)); - var proxyMethods = proxyType.GetMethods(); - - var methods = from a in apiMethods - join p in proxyMethods - on new MethodFeature(a) equals new MethodFeature(p) - let methodAttr = p.GetCustomAttribute() - where methodAttr != null - orderby methodAttr.Index - select a; - - return methods.ToArray(); - } - - /// - /// 从接口所在程序集查找代理类 - /// - /// - private static Type? FindProxyTypeFromAssembly() - { - var interfaceType = typeof(THttpApi); - foreach (var proxyType in interfaceType.Assembly.GetTypes()) + var apiMethods = HttpApi.FindApiMethods(httpApiType); + var classMethods = proxyClassType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance); + + // 按照Index特征对apiMethods进行排序 + var query = from a in apiMethods.Select(item => new MethodFeature(item, isProxyMethod: false)) + join c in classMethods.Select(item => new MethodFeature(item, isProxyMethod: true)) + on a equals c + orderby c.Index + select a.Method; + + var methods = query.ToArray(); + if (apiMethods.Length != methods.Length) { - if (proxyType.IsClass == false) - { - continue; - } - - var proxyClassAttr = proxyType.GetCustomAttribute(); - if (proxyClassAttr == null || proxyClassAttr.HttpApiType != interfaceType) - { - continue; - } - - return proxyType; + var missingMethod = apiMethods.Except(methods).FirstOrDefault(); + var message = $"{httpApiType}的代理类缺失方法{missingMethod}"; + throw new ProxyTypeException(httpApiType, message); } - - return null; + return methods; } /// @@ -112,15 +95,29 @@ orderby methodAttr.Index /// private sealed class MethodFeature : IEquatable { - private readonly MethodInfo method; + public MethodInfo Method { get; } + + public int Index { get; } + + public string Name { get; } /// /// MethodInfo的特征 /// /// - public MethodFeature(MethodInfo method) + /// + public MethodFeature(MethodInfo method, bool isProxyMethod) { - this.method = method; + this.Method = method; + + var attribute = default(HttpApiProxyMethodAttribute); + if (isProxyMethod) + { + attribute = method.GetCustomAttribute(); + } + + this.Index = attribute == null ? -1 : attribute.Index; + this.Name = attribute == null ? $"{method.DeclaringType?.FullName}.{method.Name}" : attribute.Name; } /// @@ -130,15 +127,14 @@ public MethodFeature(MethodInfo method) /// public bool Equals(MethodFeature? other) { - if (other == null) + if (other == null || this.Name != other.Name) { return false; } - var x = this.method; - var y = other.method; - - if (x.Name != y.Name || x.ReturnType != y.ReturnType) + var x = this.Method; + var y = other.Method; + if (x.ReturnType != y.ReturnType) { return false; } @@ -161,9 +157,9 @@ public override bool Equals(object? obj) public override int GetHashCode() { var hashCode = new HashCode(); - hashCode.Add(this.method.Name); - hashCode.Add(this.method.ReturnType); - foreach (var parameter in this.method.GetParameters()) + hashCode.Add(this.Name); + hashCode.Add(this.Method.ReturnType); + foreach (var parameter in this.Method.GetParameters()) { hashCode.Add(parameter.ParameterType); } diff --git a/WebApiClientCore/Implementations/SourceGeneratorProxyClassType.cs b/WebApiClientCore/Implementations/SourceGeneratorProxyClassType.cs new file mode 100644 index 00000000..4fd029e9 --- /dev/null +++ b/WebApiClientCore/Implementations/SourceGeneratorProxyClassType.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Reflection; + +namespace WebApiClientCore.Implementations +{ + /// + /// 提供获取SourceGenerator生成的代理类型 + /// + static class SourceGeneratorProxyClassType + { + private static readonly object syncRoot = new(); + private static readonly HashSet assemblies = []; + private static readonly ConcurrentDictionary httpApiProxyClassTable = []; + + /// + /// 查找指定接口类型的代理类类型 + /// + /// 接口类型 + /// + public static Type? Find(Type httpApiType) + { + AnalyzeAssembly(httpApiType.Assembly); + + if (httpApiProxyClassTable.TryGetValue(httpApiType, out var proxyClassType)) + { + return proxyClassType; + } + return null; + } + + + private static void AnalyzeAssembly(Assembly assembly) + { + if (AddAssembly(assembly)) + { + foreach (var classType in assembly.GetTypes()) + { + if (classType.IsClass) + { + var proxyClassAttr = classType.GetCustomAttribute(); + if (proxyClassAttr != null) + { + httpApiProxyClassTable.TryAdd(proxyClassAttr.HttpApiType, classType); + } + } + } + } + } + + private static bool AddAssembly(Assembly assembly) + { + lock (syncRoot) + { + return assemblies.Add(assembly); + } + } + } +}