From d5041103fe58b2043dd15397c745a7decf54ab68 Mon Sep 17 00:00:00 2001 From: "victor.x.qu" Date: Sat, 9 Jan 2021 21:36:02 +0800 Subject: [PATCH 1/5] add ut --- .../DependencyInjection/MethodTest.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/Norns.Urd.Test/DependencyInjection/MethodTest.cs b/test/Norns.Urd.Test/DependencyInjection/MethodTest.cs index c0b70c3..14b9387 100644 --- a/test/Norns.Urd.Test/DependencyInjection/MethodTest.cs +++ b/test/Norns.Urd.Test/DependencyInjection/MethodTest.cs @@ -34,6 +34,8 @@ public int DefaultNonAspectIntMethodOutParameter(out int y) } int IntMethod(); + + int IntMethod2(); } public abstract class MTest @@ -84,6 +86,8 @@ public class MMTest : IMTest { public int IntMethod() => 6; + int IMTest.IntMethod2() => 88; + [NonAspect] public virtual int NonAspectIntMethod() => 3; @@ -146,6 +150,16 @@ public void InterfaceWhenDefaultNonAspectIntMethodOutParameter() Assert.Equal(4, y); } + [Fact] + public void InterfaceWhenPrivateIntMethod2() + { + var p = AopTestExtensions.ConfigServiceCollectionWithAop(i => i.AddTransient()) + .GetRequiredService(); + var pt = p.GetType(); + Assert.True(pt.IsProxyType()); + Assert.Equal(88, p.IntMethod2()); + } + #endregion Interface #region abstract class From 282559e3e33f2113bc1d586a46018e965fd56ad6 Mon Sep 17 00:00:00 2001 From: "Victor.X.Qu" Date: Wed, 26 May 2021 22:13:54 +0800 Subject: [PATCH 2/5] fix bug about interface Attribute --- ProjectCommon.targets | 2 +- .../Attributes/CacheAttribute.cs | 2 +- .../DynamicProxy/FacadeProxyBuilder.cs | 2 +- .../DynamicProxy/InheritProxyBuilder.cs | 8 ++++- .../DynamicProxy/ProxyBuilderBase.cs | 4 +-- .../Controllers/WeatherForecastController.cs | 6 ++-- .../Norns.Urd.Test/Caching/MemoryCacheTest.cs | 31 +++++++++++++++++-- 7 files changed, 44 insertions(+), 11 deletions(-) diff --git a/ProjectCommon.targets b/ProjectCommon.targets index ca1f6cc..023ac88 100644 --- a/ProjectCommon.targets +++ b/ProjectCommon.targets @@ -3,7 +3,7 @@ net5.0;netstandard2.1;netstandard2.0;net461 true $(VersionSuffix) - 0.0.4.4 + 0.0.4.5 $(Version) $(Version) $(Version)-$(VersionSuffix) diff --git a/src/Norns.Urd.Caching.Abstractions/Attributes/CacheAttribute.cs b/src/Norns.Urd.Caching.Abstractions/Attributes/CacheAttribute.cs index cde3b87..dec448b 100644 --- a/src/Norns.Urd.Caching.Abstractions/Attributes/CacheAttribute.cs +++ b/src/Norns.Urd.Caching.Abstractions/Attributes/CacheAttribute.cs @@ -2,7 +2,7 @@ namespace Norns.Urd.Caching { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)] public class CacheAttribute : Attribute { private readonly ICacheOptionGenerator optionGenerator; diff --git a/src/Norns.Urd/DynamicProxy/FacadeProxyBuilder.cs b/src/Norns.Urd/DynamicProxy/FacadeProxyBuilder.cs index 444b7c4..eaf8b24 100644 --- a/src/Norns.Urd/DynamicProxy/FacadeProxyBuilder.cs +++ b/src/Norns.Urd/DynamicProxy/FacadeProxyBuilder.cs @@ -21,7 +21,7 @@ protected override void CallPropertyInjectInConstructor(in ProxyGeneratorContext // no call PropertyInject In Constructor } - protected override FieldBuilder DefineMethodInfoCaller(in ProxyGeneratorContext context, MethodInfo method) + protected override FieldBuilder DefineMethodInfoCaller(in ProxyGeneratorContext context, MethodInfo method, MethodInfo serviceMethod) { return context.AssistType.DefineMethodInfoCaller(method, method.GetReflector().DisplayName); } diff --git a/src/Norns.Urd/DynamicProxy/InheritProxyBuilder.cs b/src/Norns.Urd/DynamicProxy/InheritProxyBuilder.cs index c920f8f..1be714b 100644 --- a/src/Norns.Urd/DynamicProxy/InheritProxyBuilder.cs +++ b/src/Norns.Urd/DynamicProxy/InheritProxyBuilder.cs @@ -17,7 +17,7 @@ protected override void CallPropertyInjectInConstructor(in ProxyGeneratorContext il.Emit(OpCodes.Call, context.ProxyType.PropertyInject.setter); } - protected override FieldBuilder DefineMethodInfoCaller(in ProxyGeneratorContext context, MethodInfo method) + protected override FieldBuilder DefineMethodInfoCaller(in ProxyGeneratorContext context, MethodInfo method, MethodInfo serviceMethod) { var baseMethodName = $"{method.GetReflector().DisplayName}_Base"; if (!context.ProxyType.Methods.TryGetValue(baseMethodName, out var methodBaseBuilder)) @@ -28,6 +28,12 @@ protected override FieldBuilder DefineMethodInfoCaller(in ProxyGeneratorContext methodBaseBuilder.DefineGenericParameter(method); methodBaseBuilder.DefineParameters(method); methodBaseBuilder.DefineCustomAttributes(method); + var serviceMethodReflector = method.GetReflector(); + foreach (var customAttributeData in serviceMethod.CustomAttributes + .Where(i => !i.AttributeType.IsSubclassOf(typeof(AbstractInterceptorAttribute)) && !serviceMethodReflector.IsDefined(i.AttributeType))) + { + methodBaseBuilder.SetCustomAttribute(customAttributeData.DefineCustomAttribute()); + } var il = methodBaseBuilder.GetILGenerator(); if (method.IsAbstract) { diff --git a/src/Norns.Urd/DynamicProxy/ProxyBuilderBase.cs b/src/Norns.Urd/DynamicProxy/ProxyBuilderBase.cs index 76621c9..398e3bd 100644 --- a/src/Norns.Urd/DynamicProxy/ProxyBuilderBase.cs +++ b/src/Norns.Urd/DynamicProxy/ProxyBuilderBase.cs @@ -166,7 +166,7 @@ private MethodBuilder DefineMethod(in ProxyGeneratorContext context, MethodInfo protected abstract void GetServiceInstance(in ProxyGeneratorContext context, ILGenerator il); - protected abstract FieldBuilder DefineMethodInfoCaller(in ProxyGeneratorContext context, MethodInfo method); + protected abstract FieldBuilder DefineMethodInfoCaller(in ProxyGeneratorContext context, MethodInfo method, MethodInfo serviceMethod); protected abstract void CallPropertyInjectInConstructor(in ProxyGeneratorContext context, ILGenerator il); @@ -180,7 +180,7 @@ protected MethodBuilder DefineProxyMethod(in ProxyGeneratorContext context, Meth methodBuilder.DefineParameters(method); methodBuilder.DefineCustomAttributes(method); var il = methodBuilder.GetILGenerator(); - var caller = DefineMethodInfoCaller(context, implementationMethod); + var caller = DefineMethodInfoCaller(context, implementationMethod, method); if (method.ContainsGenericParameters && !method.IsGenericMethodDefinition) { il.Emit(OpCodes.Ldsfld, caller); diff --git a/test/Examples.WebApi/Controllers/WeatherForecastController.cs b/test/Examples.WebApi/Controllers/WeatherForecastController.cs index 7624a5f..193dcfc 100644 --- a/test/Examples.WebApi/Controllers/WeatherForecastController.cs +++ b/test/Examples.WebApi/Controllers/WeatherForecastController.cs @@ -5,11 +5,13 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using Norns.Urd.Caching; namespace Examples.WebApi.Controllers { public interface IAopTest { + [Cache(nameof(Get), AbsoluteExpirationRelativeToNow = "00:00:05")] IEnumerable Get(); } @@ -63,10 +65,10 @@ public async Task TestUploadStream() [HttpGet("file")] public async Task TestDownloadStream() { - HttpContext.Response.ContentType = "application/octet-stream"; + HttpContext.Response.ContentType = "application/octet-stream"; var bytes = System.Text.Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()); await HttpContext.Response.Body.WriteAsync(bytes, 0, bytes.Length); - await HttpContext.Response.CompleteAsync(); + await HttpContext.Response.CompleteAsync(); } } } \ No newline at end of file diff --git a/test/Norns.Urd.Test/Caching/MemoryCacheTest.cs b/test/Norns.Urd.Test/Caching/MemoryCacheTest.cs index 3248a4e..aa105c4 100644 --- a/test/Norns.Urd.Test/Caching/MemoryCacheTest.cs +++ b/test/Norns.Urd.Test/Caching/MemoryCacheTest.cs @@ -23,7 +23,13 @@ public CacheOptions Generate(AspectContext context) } } - public class DoCacheTest + public interface IDoCacheTest + { + [Cache(nameof(DoInterfaceint), AbsoluteExpirationRelativeToNow = "00:00:02")] + int DoInterfaceint(int count); + } + + public class DoCacheTest : IDoCacheTest { public int Count { get; set; } @@ -64,15 +70,34 @@ public virtual ValueTask DoValueTaskAsync(int count) Count = count; return new ValueTask(Task.CompletedTask); } + + public int DoInterfaceint(int count) + { + return count; + } } - public DoCacheTest Mock() + public T Mock() where T : IDoCacheTest { return new ServiceCollection() .AddTransient() + .AddTransient() .ConfigureAop(i => i.EnableMemoryCache()) .BuildServiceProvider() - .GetRequiredService(); + .GetRequiredService(); + } + + public DoCacheTest Mock() + { + return Mock(); + } + + [Fact] + public void CacheWhenInterfaceSyncNoReturnValue() + { + var sut = Mock(); + Assert.Equal(3, sut.DoInterfaceint(3)); + Assert.Equal(3, sut.DoInterfaceint(5)); } [Fact] From 11e87281580c36dfdf81b2460defb5cf2db1fcfd Mon Sep 17 00:00:00 2001 From: "victor.x.qu" Date: Wed, 26 May 2021 22:14:54 +0800 Subject: [PATCH 3/5] fix bug about interface attribute --- .../CacheInterceptor.cs | 154 ++++---- .../DynamicProxy/InheritProxyBuilder.cs | 8 +- .../Reflection/Extensions/MethodExtensions.cs | 348 +++++++++--------- src/Norns.Urd/Reflection/MemberReflector.cs | 42 +-- src/Norns.Urd/Reflection/MethodReflector.cs | 150 ++++---- .../Controllers/WeatherForecastController.cs | 2 +- .../Norns.Urd.Test/Caching/MemoryCacheTest.cs | 12 +- 7 files changed, 358 insertions(+), 358 deletions(-) diff --git a/src/Norns.Urd.Caching.Abstractions/CacheInterceptor.cs b/src/Norns.Urd.Caching.Abstractions/CacheInterceptor.cs index a7f2504..e31d640 100644 --- a/src/Norns.Urd.Caching.Abstractions/CacheInterceptor.cs +++ b/src/Norns.Urd.Caching.Abstractions/CacheInterceptor.cs @@ -1,78 +1,78 @@ -using Microsoft.Extensions.DependencyInjection; -using Norns.Urd.Reflection; -using System; -using System.Collections.Concurrent; -using System.Linq; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; - -namespace Norns.Urd.Caching -{ - public class CacheInterceptor : AbstractInterceptor - { - private static readonly ConcurrentDictionary> syncCache = new ConcurrentDictionary>(); - private static readonly ConcurrentDictionary> asyncCache = new ConcurrentDictionary>(); - - public override int Order { get; set; } = -95000; - - public override bool CanAspect(MethodReflector method) - { - return method.IsDefined(); - } - - public override void Invoke(AspectContext context, AspectDelegate next) - { - syncCache.GetOrAdd(context.Method, CreateInvoke)(context, next); - } - - private Action CreateInvoke(MethodInfo method) - { - var returnType = method.ReturnType; - if (returnType == typeof(void)) - { - return (c, next) => next(c); - } - var optionCreators = method.GetReflector() - .GetCustomAttributes() - .OrderBy(i => i.Order) - .Select(i => i.GetCacheOptionCreator()) - .ToArray(); - var pt = typeof(ICacheProvider<>).MakeGenericType(returnType); - return (context, next) => - { - var p = context.ServiceProvider.GetRequiredService(pt) as ICacheProvider; - context.ReturnValue = p.GetOrCreateValue(optionCreators, context, next); - }; - } - - public override async Task InvokeAsync(AspectContext context, AsyncAspectDelegate next) - { - await asyncCache.GetOrAdd(context.Method, CreateInvokeAsync)(context, next); - } - - private static Func CreateInvokeAsync(MethodInfo method) - { - var returnType = method.ReturnType; - if (returnType == typeof(Task) || returnType == typeof(ValueTask)) - { - return (c, next) => next(c); - } - var mr = method.GetReflector(); - var optionCreators = mr - .GetCustomAttributes() - .OrderBy(i => i.Order) - .Select(i => i.GetCacheOptionCreator()) - .ToArray(); - var cti = mr.CancellationTokenIndex; - var pt = typeof(ICacheProvider<>).MakeGenericType(returnType.GetGenericArguments()[0]); - var isReturnValueTask = method.IsReturnValueTask(); - return async (context, next) => - { - var token = cti < 0 ? CancellationToken.None : (CancellationToken)context.Parameters[cti]; - var p = context.ServiceProvider.GetRequiredService(pt) as ICacheProvider; - context.ReturnValue = await p.GetOrCreateValueAsync(optionCreators, context, next, token, isReturnValueTask); - }; - } - } +using Microsoft.Extensions.DependencyInjection; +using Norns.Urd.Reflection; +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; + +namespace Norns.Urd.Caching +{ + public class CacheInterceptor : AbstractInterceptor + { + private static readonly ConcurrentDictionary> syncCache = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> asyncCache = new ConcurrentDictionary>(); + + public override int Order { get; set; } = -95000; + + public override bool CanAspect(MethodReflector method) + { + return method.IsDefined(); + } + + public override void Invoke(AspectContext context, AspectDelegate next) + { + syncCache.GetOrAdd(context.Method, CreateInvoke)(context, next); + } + + private Action CreateInvoke(MethodInfo method) + { + var returnType = method.ReturnType; + if (returnType == typeof(void)) + { + return (c, next) => next(c); + } + var optionCreators = method.GetReflector() + .GetCustomAttributes() + .OrderBy(i => i.Order) + .Select(i => i.GetCacheOptionCreator()) + .ToArray(); + var pt = typeof(ICacheProvider<>).MakeGenericType(returnType); + return (context, next) => + { + var p = context.ServiceProvider.GetRequiredService(pt) as ICacheProvider; + context.ReturnValue = p.GetOrCreateValue(optionCreators, context, next); + }; + } + + public override async Task InvokeAsync(AspectContext context, AsyncAspectDelegate next) + { + await asyncCache.GetOrAdd(context.Method, CreateInvokeAsync)(context, next); + } + + private static Func CreateInvokeAsync(MethodInfo method) + { + var returnType = method.ReturnType; + if (returnType == typeof(Task) || returnType == typeof(ValueTask)) + { + return (c, next) => next(c); + } + var mr = method.GetReflector(); + var optionCreators = mr + .GetCustomAttributes() + .OrderBy(i => i.Order) + .Select(i => i.GetCacheOptionCreator()) + .ToArray(); + var cti = mr.CancellationTokenIndex; + var pt = typeof(ICacheProvider<>).MakeGenericType(returnType.GetGenericArguments()[0]); + var isReturnValueTask = method.IsReturnValueTask(); + return async (context, next) => + { + var token = cti < 0 ? CancellationToken.None : (CancellationToken)context.Parameters[cti]; + var p = context.ServiceProvider.GetRequiredService(pt) as ICacheProvider; + context.ReturnValue = await p.GetOrCreateValueAsync(optionCreators, context, next, token, isReturnValueTask); + }; + } + } } \ No newline at end of file diff --git a/src/Norns.Urd/DynamicProxy/InheritProxyBuilder.cs b/src/Norns.Urd/DynamicProxy/InheritProxyBuilder.cs index 1be714b..1c4c8fd 100644 --- a/src/Norns.Urd/DynamicProxy/InheritProxyBuilder.cs +++ b/src/Norns.Urd/DynamicProxy/InheritProxyBuilder.cs @@ -29,10 +29,10 @@ protected override FieldBuilder DefineMethodInfoCaller(in ProxyGeneratorContext methodBaseBuilder.DefineParameters(method); methodBaseBuilder.DefineCustomAttributes(method); var serviceMethodReflector = method.GetReflector(); - foreach (var customAttributeData in serviceMethod.CustomAttributes - .Where(i => !i.AttributeType.IsSubclassOf(typeof(AbstractInterceptorAttribute)) && !serviceMethodReflector.IsDefined(i.AttributeType))) - { - methodBaseBuilder.SetCustomAttribute(customAttributeData.DefineCustomAttribute()); + foreach (var customAttributeData in serviceMethod.CustomAttributes + .Where(i => !i.AttributeType.IsSubclassOf(typeof(AbstractInterceptorAttribute)) && !serviceMethodReflector.IsDefined(i.AttributeType))) + { + methodBaseBuilder.SetCustomAttribute(customAttributeData.DefineCustomAttribute()); } var il = methodBaseBuilder.GetILGenerator(); if (method.IsAbstract) diff --git a/src/Norns.Urd/Reflection/Extensions/MethodExtensions.cs b/src/Norns.Urd/Reflection/Extensions/MethodExtensions.cs index b5e1c6b..83edcf2 100644 --- a/src/Norns.Urd/Reflection/Extensions/MethodExtensions.cs +++ b/src/Norns.Urd/Reflection/Extensions/MethodExtensions.cs @@ -1,175 +1,175 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Reflection.Emit; -using System.Runtime.CompilerServices; -using System.Threading; - -namespace Norns.Urd.Reflection -{ - public static class MethodExtensions - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static MethodReflector Create(MethodInfo t) => new MethodReflector(t); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MethodReflector GetReflector(this MethodInfo method) - { - return ReflectorCache.GetOrAdd(method, Create); - } - - public static bool IsNotPropertyBinding(this MethodInfo method) => method.GetReflector().BindingProperty == null; - - public static bool IsVisibleAndVirtual(this MethodInfo method) - { - if (method.IsStatic || method.IsFinal) - { - return false; - } - return method.IsVirtual && - (method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly); - } - - public static bool IsVoid(this MethodInfo methodInfo) => methodInfo.ReturnType == typeof(void); - - public static bool IsAsync(this MethodInfo methodInfo) - { - var returnType = methodInfo.ReturnType.GetTypeInfo(); - return returnType.IsTask() || returnType.IsTaskWithResult() || returnType.IsValueTask() || returnType.IsValueTaskWithResult(); - } - - public static bool IsTask(this MethodInfo methodInfo) - { - var returnType = methodInfo.ReturnType.GetTypeInfo(); - return returnType.IsTask(); - } - - public static bool IsValueTask(this MethodInfo methodInfo) - { - var returnType = methodInfo.ReturnType.GetTypeInfo(); - return returnType.IsValueTask(); - } - - public static bool IsReturnTask(this MethodInfo methodInfo) - { - var returnType = methodInfo.ReturnType.GetTypeInfo(); - return returnType.IsTaskWithResult(); - } - - public static bool IsReturnValueTask(this MethodInfo methodInfo) - { - var returnType = methodInfo.ReturnType.GetTypeInfo(); - return returnType.IsValueTaskWithResult(); - } - - public static bool IsExplicit(this MethodInfo methodInfo) - { - return methodInfo.Attributes.HasFlag(MethodAttributes.Private | MethodAttributes.Final | - MethodAttributes.Virtual); - } - - public static bool IsCallvirt(this MethodInfo methodInfo) - { - return methodInfo.IsExplicit() - || !methodInfo.DeclaringType.GetTypeInfo().IsClass; - } - - public static void DefineGenericParameter(this MethodBuilder methodBuilder, MethodInfo tergetMethod) - { - if (!tergetMethod.IsGenericMethod) - { - return; - } - var genericArguments = tergetMethod.GetGenericArguments().Select(t => t.GetTypeInfo()).ToArray(); - var genericArgumentsBuilders = methodBuilder.DefineGenericParameters(genericArguments.Select(a => a.Name).ToArray()); - for (var index = 0; index < genericArguments.Length; index++) - { - genericArgumentsBuilders[index].SetGenericParameterAttributes(genericArguments[index].GenericParameterAttributes); - foreach (var constraint in genericArguments[index].GetGenericParameterConstraints().Select(t => t.GetTypeInfo())) - { - if (constraint.IsClass) genericArgumentsBuilders[index].SetBaseTypeConstraint(constraint.AsType()); - if (constraint.IsInterface) genericArgumentsBuilders[index].SetInterfaceConstraints(constraint.AsType()); - } - } - } - - public static void DefineParameters(this MethodBuilder methodBuilder, MethodInfo targetMethod) - { - var parameters = targetMethod.GetParameters(); - if (parameters.Length > 0) - { - const int paramOffset = 1; // 1 - foreach (var parameter in parameters) - { - var parameterBuilder = methodBuilder.DefineParameter(parameter.Position + paramOffset, parameter.Attributes, parameter.Name); - if (parameter.HasDefaultValueByAttributes()) - { - try - { - parameterBuilder.CopyDefaultValueConstant(parameter); - } - catch - { - // Default value replication is a nice-to-have feature but not essential, - // so if it goes wrong for one parameter, just continue. - } - } - foreach (var attribute in parameter.CustomAttributes) - { - parameterBuilder.SetCustomAttribute(attribute.DefineCustomAttribute()); - } - } - } - - var returnParamter = targetMethod.ReturnParameter; - var returnParameterBuilder = methodBuilder.DefineParameter(0, returnParamter.Attributes, returnParamter.Name); - foreach (var attribute in returnParamter.CustomAttributes) - { - returnParameterBuilder.SetCustomAttribute(attribute.DefineCustomAttribute()); - } - } - - public static void DefineCustomAttributes(this MethodBuilder methodBuilder, MethodInfo method) - { - foreach (var customAttributeData in method.CustomAttributes) - { - methodBuilder.SetCustomAttribute(customAttributeData.DefineCustomAttribute()); - } - } - - public static MethodInfo GetMethod(Expression expression) - { - if (!(expression.Body is MethodCallExpression methodCallExpression)) - { - throw new InvalidCastException("Cannot be converted to MethodCallExpression"); - } - return methodCallExpression.Method; - } - - public static T CreateDelegate(this MethodInfo method, Type returnType, Type[] parameters, Action doIL) where T : Delegate - { - var dynamicMethod = new DynamicMethod($"invoker-{Guid.NewGuid():N}", returnType, parameters, method.Module, true); - var il = dynamicMethod.GetILGenerator(); - il.EmitThis(); - doIL(il); - il.EmitCallMethod(method); - il.EmitConvertTo(method.ReturnType, returnType); - il.Emit(OpCodes.Ret); - return (T)dynamicMethod.CreateDelegate(typeof(T)); - } - - public static Func CreateCancellationTokenGetter(this MethodReflector method) - { - var cancellationTokenIndex = method.CancellationTokenIndex; - if (cancellationTokenIndex > -1) - { - return context => (CancellationToken)context.Parameters[cancellationTokenIndex]; - } - else - { - return context => CancellationToken.None; - } - } - } +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Norns.Urd.Reflection +{ + public static class MethodExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static MethodReflector Create(MethodInfo t) => new MethodReflector(t); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MethodReflector GetReflector(this MethodInfo method) + { + return ReflectorCache.GetOrAdd(method, Create); + } + + public static bool IsNotPropertyBinding(this MethodInfo method) => method.GetReflector().BindingProperty == null; + + public static bool IsVisibleAndVirtual(this MethodInfo method) + { + if (method.IsStatic || method.IsFinal) + { + return false; + } + return method.IsVirtual && + (method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly); + } + + public static bool IsVoid(this MethodInfo methodInfo) => methodInfo.ReturnType == typeof(void); + + public static bool IsAsync(this MethodInfo methodInfo) + { + var returnType = methodInfo.ReturnType.GetTypeInfo(); + return returnType.IsTask() || returnType.IsTaskWithResult() || returnType.IsValueTask() || returnType.IsValueTaskWithResult(); + } + + public static bool IsTask(this MethodInfo methodInfo) + { + var returnType = methodInfo.ReturnType.GetTypeInfo(); + return returnType.IsTask(); + } + + public static bool IsValueTask(this MethodInfo methodInfo) + { + var returnType = methodInfo.ReturnType.GetTypeInfo(); + return returnType.IsValueTask(); + } + + public static bool IsReturnTask(this MethodInfo methodInfo) + { + var returnType = methodInfo.ReturnType.GetTypeInfo(); + return returnType.IsTaskWithResult(); + } + + public static bool IsReturnValueTask(this MethodInfo methodInfo) + { + var returnType = methodInfo.ReturnType.GetTypeInfo(); + return returnType.IsValueTaskWithResult(); + } + + public static bool IsExplicit(this MethodInfo methodInfo) + { + return methodInfo.Attributes.HasFlag(MethodAttributes.Private | MethodAttributes.Final | + MethodAttributes.Virtual); + } + + public static bool IsCallvirt(this MethodInfo methodInfo) + { + return methodInfo.IsExplicit() + || !methodInfo.DeclaringType.GetTypeInfo().IsClass; + } + + public static void DefineGenericParameter(this MethodBuilder methodBuilder, MethodInfo tergetMethod) + { + if (!tergetMethod.IsGenericMethod) + { + return; + } + var genericArguments = tergetMethod.GetGenericArguments().Select(t => t.GetTypeInfo()).ToArray(); + var genericArgumentsBuilders = methodBuilder.DefineGenericParameters(genericArguments.Select(a => a.Name).ToArray()); + for (var index = 0; index < genericArguments.Length; index++) + { + genericArgumentsBuilders[index].SetGenericParameterAttributes(genericArguments[index].GenericParameterAttributes); + foreach (var constraint in genericArguments[index].GetGenericParameterConstraints().Select(t => t.GetTypeInfo())) + { + if (constraint.IsClass) genericArgumentsBuilders[index].SetBaseTypeConstraint(constraint.AsType()); + if (constraint.IsInterface) genericArgumentsBuilders[index].SetInterfaceConstraints(constraint.AsType()); + } + } + } + + public static void DefineParameters(this MethodBuilder methodBuilder, MethodInfo targetMethod) + { + var parameters = targetMethod.GetParameters(); + if (parameters.Length > 0) + { + const int paramOffset = 1; // 1 + foreach (var parameter in parameters) + { + var parameterBuilder = methodBuilder.DefineParameter(parameter.Position + paramOffset, parameter.Attributes, parameter.Name); + if (parameter.HasDefaultValueByAttributes()) + { + try + { + parameterBuilder.CopyDefaultValueConstant(parameter); + } + catch + { + // Default value replication is a nice-to-have feature but not essential, + // so if it goes wrong for one parameter, just continue. + } + } + foreach (var attribute in parameter.CustomAttributes) + { + parameterBuilder.SetCustomAttribute(attribute.DefineCustomAttribute()); + } + } + } + + var returnParamter = targetMethod.ReturnParameter; + var returnParameterBuilder = methodBuilder.DefineParameter(0, returnParamter.Attributes, returnParamter.Name); + foreach (var attribute in returnParamter.CustomAttributes) + { + returnParameterBuilder.SetCustomAttribute(attribute.DefineCustomAttribute()); + } + } + + public static void DefineCustomAttributes(this MethodBuilder methodBuilder, MethodInfo method) + { + foreach (var customAttributeData in method.CustomAttributes) + { + methodBuilder.SetCustomAttribute(customAttributeData.DefineCustomAttribute()); + } + } + + public static MethodInfo GetMethod(Expression expression) + { + if (!(expression.Body is MethodCallExpression methodCallExpression)) + { + throw new InvalidCastException("Cannot be converted to MethodCallExpression"); + } + return methodCallExpression.Method; + } + + public static T CreateDelegate(this MethodInfo method, Type returnType, Type[] parameters, Action doIL) where T : Delegate + { + var dynamicMethod = new DynamicMethod($"invoker-{Guid.NewGuid():N}", returnType, parameters, method.Module, true); + var il = dynamicMethod.GetILGenerator(); + il.EmitThis(); + doIL(il); + il.EmitCallMethod(method); + il.EmitConvertTo(method.ReturnType, returnType); + il.Emit(OpCodes.Ret); + return (T)dynamicMethod.CreateDelegate(typeof(T)); + } + + public static Func CreateCancellationTokenGetter(this MethodReflector method) + { + var cancellationTokenIndex = method.CancellationTokenIndex; + if (cancellationTokenIndex > -1) + { + return context => (CancellationToken)context.Parameters[cancellationTokenIndex]; + } + else + { + return context => CancellationToken.None; + } + } + } } \ No newline at end of file diff --git a/src/Norns.Urd/Reflection/MemberReflector.cs b/src/Norns.Urd/Reflection/MemberReflector.cs index cd35369..d6d2c14 100644 --- a/src/Norns.Urd/Reflection/MemberReflector.cs +++ b/src/Norns.Urd/Reflection/MemberReflector.cs @@ -1,22 +1,22 @@ -using System.Linq; -using System.Reflection; - -namespace Norns.Urd.Reflection -{ - public interface ICustomAttributeReflectorProvider - { - CustomAttributeReflector[] CustomAttributeReflectors { get; } - } - - public class MemberReflector : ICustomAttributeReflectorProvider where TMemberInfo : MemberInfo - { - public TMemberInfo MemberInfo { get; } - public CustomAttributeReflector[] CustomAttributeReflectors { get; } - - public MemberReflector(TMemberInfo memberInfo) - { - MemberInfo = memberInfo; - CustomAttributeReflectors = memberInfo.CustomAttributes.Select(data => CustomAttributeReflector.Create(data)).ToArray(); - } - } +using System.Linq; +using System.Reflection; + +namespace Norns.Urd.Reflection +{ + public interface ICustomAttributeReflectorProvider + { + CustomAttributeReflector[] CustomAttributeReflectors { get; } + } + + public class MemberReflector : ICustomAttributeReflectorProvider where TMemberInfo : MemberInfo + { + public TMemberInfo MemberInfo { get; } + public CustomAttributeReflector[] CustomAttributeReflectors { get; } + + public MemberReflector(TMemberInfo memberInfo) + { + MemberInfo = memberInfo; + CustomAttributeReflectors = memberInfo.CustomAttributes.Select(data => CustomAttributeReflector.Create(data)).ToArray(); + } + } } \ No newline at end of file diff --git a/src/Norns.Urd/Reflection/MethodReflector.cs b/src/Norns.Urd/Reflection/MethodReflector.cs index 1d9839e..27a2aba 100644 --- a/src/Norns.Urd/Reflection/MethodReflector.cs +++ b/src/Norns.Urd/Reflection/MethodReflector.cs @@ -1,76 +1,76 @@ -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading; - -namespace Norns.Urd.Reflection -{ - public class MethodReflector : MemberReflector - { - public string DisplayName { get; } - public PropertyInfo BindingProperty { get; } - public ParameterReflector[] Parameters { get; } - - public int CancellationTokenIndex { get; } - - public MethodReflector(MethodInfo methodInfo) : base(methodInfo) - { - BindingProperty = GetBindingProperty(methodInfo); - DisplayName = GetDisplayName(methodInfo); - var cancellationTokenIndex = -1; - Parameters = methodInfo.GetParameters().Select(i => - { - if (i.ParameterType == typeof(CancellationToken)) - { - cancellationTokenIndex = i.Position; - } - return new ParameterReflector(i); - }).ToArray(); - CancellationTokenIndex = cancellationTokenIndex; - } - - private static PropertyInfo GetBindingProperty(MethodInfo method) - { - foreach (var item in method.DeclaringType.GetTypeInfo().GetProperties()) - { - if (item.CanRead && item.GetMethod == method) return item; - if (item.CanWrite && item.SetMethod == method) return item; - } - return null; - } - - private static string GetDisplayName(MethodInfo method) - { - var name = new StringBuilder(method.ReturnType.GetReflector().DisplayName) - .Append(' ') - .Append(method.Name); - if (method.IsGenericMethod) - { - name.Append('<'); - var arguments = method.GetGenericArguments(); - name.Append(arguments[0].GetReflector().DisplayName); - for (var i = 1; i < arguments.Length; i++) - { - name.Append(','); - name.Append(arguments[i].GetReflector().DisplayName); - } - name.Append('>'); - } - var parameterTypes = method.GetParameters().Select(i => i.ParameterType).ToArray(); - name.Append('('); - if (parameterTypes.Length == 0) - { - name.Append(')'); - return name.ToString(); - } - name.Append(parameterTypes[0].GetReflector().DisplayName); - for (var i = 1; i < parameterTypes.Length; i++) - { - name.Append(','); - name.Append(parameterTypes[i].GetReflector().DisplayName); - } - name.Append(')'); - return name.ToString(); - } - } +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; + +namespace Norns.Urd.Reflection +{ + public class MethodReflector : MemberReflector + { + public string DisplayName { get; } + public PropertyInfo BindingProperty { get; } + public ParameterReflector[] Parameters { get; } + + public int CancellationTokenIndex { get; } + + public MethodReflector(MethodInfo methodInfo) : base(methodInfo) + { + BindingProperty = GetBindingProperty(methodInfo); + DisplayName = GetDisplayName(methodInfo); + var cancellationTokenIndex = -1; + Parameters = methodInfo.GetParameters().Select(i => + { + if (i.ParameterType == typeof(CancellationToken)) + { + cancellationTokenIndex = i.Position; + } + return new ParameterReflector(i); + }).ToArray(); + CancellationTokenIndex = cancellationTokenIndex; + } + + private static PropertyInfo GetBindingProperty(MethodInfo method) + { + foreach (var item in method.DeclaringType.GetTypeInfo().GetProperties()) + { + if (item.CanRead && item.GetMethod == method) return item; + if (item.CanWrite && item.SetMethod == method) return item; + } + return null; + } + + private static string GetDisplayName(MethodInfo method) + { + var name = new StringBuilder(method.ReturnType.GetReflector().DisplayName) + .Append(' ') + .Append(method.Name); + if (method.IsGenericMethod) + { + name.Append('<'); + var arguments = method.GetGenericArguments(); + name.Append(arguments[0].GetReflector().DisplayName); + for (var i = 1; i < arguments.Length; i++) + { + name.Append(','); + name.Append(arguments[i].GetReflector().DisplayName); + } + name.Append('>'); + } + var parameterTypes = method.GetParameters().Select(i => i.ParameterType).ToArray(); + name.Append('('); + if (parameterTypes.Length == 0) + { + name.Append(')'); + return name.ToString(); + } + name.Append(parameterTypes[0].GetReflector().DisplayName); + for (var i = 1; i < parameterTypes.Length; i++) + { + name.Append(','); + name.Append(parameterTypes[i].GetReflector().DisplayName); + } + name.Append(')'); + return name.ToString(); + } + } } \ No newline at end of file diff --git a/test/Examples.WebApi/Controllers/WeatherForecastController.cs b/test/Examples.WebApi/Controllers/WeatherForecastController.cs index 193dcfc..0fef343 100644 --- a/test/Examples.WebApi/Controllers/WeatherForecastController.cs +++ b/test/Examples.WebApi/Controllers/WeatherForecastController.cs @@ -24,7 +24,7 @@ public class AopTest : IAopTest, IDisposable public void Dispose() { - } + } public IEnumerable Get() { diff --git a/test/Norns.Urd.Test/Caching/MemoryCacheTest.cs b/test/Norns.Urd.Test/Caching/MemoryCacheTest.cs index aa105c4..2f9233e 100644 --- a/test/Norns.Urd.Test/Caching/MemoryCacheTest.cs +++ b/test/Norns.Urd.Test/Caching/MemoryCacheTest.cs @@ -69,12 +69,12 @@ public virtual ValueTask DoValueTaskAsync(int count) { Count = count; return new ValueTask(Task.CompletedTask); - } - - public int DoInterfaceint(int count) - { - return count; - } + } + + public int DoInterfaceint(int count) + { + return count; + } } public T Mock() where T : IDoCacheTest From 2fc56d7648a5e442c2563112a085e9053767dc08 Mon Sep 17 00:00:00 2001 From: "Victor.X.Qu (g-mis.cncd02.Newegg) 42377" Date: Wed, 9 Mar 2022 15:24:35 +0800 Subject: [PATCH 4/5] update --- Norns.sln | 13 +++--------- ProjectCommon.targets | 6 +++--- .../Norns.Urd.Caching.Abstractions.csproj | 4 ++-- .../Norns.Urd.Caching.DistributedCache.csproj | 2 +- .../Norns.Urd.Caching.Memory.csproj | 4 ++-- .../Norns.Urd.Extensions.Polly.csproj | 2 +- ...Norns.Urd.HttpClient.NewtonsoftJson.csproj | 4 ++-- .../Norns.Urd.HttpClient.csproj | 12 +++++------ src/Norns.Urd/Linq/EnumerableExtensions.cs | 21 +++++++++++-------- src/Norns.Urd/Norns.Urd.csproj | 4 ++-- test/Examples.WebApi/Examples.WebApi.csproj | 2 +- test/Norns.Urd.Test/Norns.Urd.Test.csproj | 4 ++-- test/SimpleBenchmark/SimpleBenchmark.csproj | 6 +++--- 13 files changed, 40 insertions(+), 44 deletions(-) diff --git a/Norns.sln b/Norns.sln index c1b8f54..125afb6 100644 --- a/Norns.sln +++ b/Norns.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32210.238 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3FD69CA3-BAF7-4B7A-AAB2-B9853759F4D7}" EndProject @@ -19,8 +19,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Norns.Urd.Test", "test\Norn EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.WebApi", "test\Examples.WebApi\Examples.WebApi.csproj", "{DA8A1EF3-7E2B-4EE6-AE2C-57902F5A3E94}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleBenchmark", "test\SimpleBenchmark\SimpleBenchmark.csproj", "{E432067A-D04B-4021-A6A2-4E62EE83139A}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Norns.Urd.Extensions.Polly", "src\Norns.Urd.Extensions.Polly\Norns.Urd.Extensions.Polly.csproj", "{F55592A9-DBCC-4B9A-BA25-205CEC8A641A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Norns.Urd.Caching.Memory", "src\Norns.Urd.Caching.Memory\Norns.Urd.Caching.Memory.csproj", "{60AE7517-C5EF-479E-87B7-2BDF2AF45A61}" @@ -35,7 +33,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Norns.Urd.HttpClient", "src EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HttpClients", "HttpClients", "{6564067F-6756-4C2A-8E96-5CC3D4E40147}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Norns.Urd.HttpClient.NewtonsoftJson", "src\Norns.Urd.HttpClient.NewtonsoftJson\Norns.Urd.HttpClient.NewtonsoftJson.csproj", "{4DD4DA90-0129-4D05-903A-A454AEE9BED5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Norns.Urd.HttpClient.NewtonsoftJson", "src\Norns.Urd.HttpClient.NewtonsoftJson\Norns.Urd.HttpClient.NewtonsoftJson.csproj", "{4DD4DA90-0129-4D05-903A-A454AEE9BED5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -55,10 +53,6 @@ Global {DA8A1EF3-7E2B-4EE6-AE2C-57902F5A3E94}.Debug|Any CPU.Build.0 = Debug|Any CPU {DA8A1EF3-7E2B-4EE6-AE2C-57902F5A3E94}.Release|Any CPU.ActiveCfg = Release|Any CPU {DA8A1EF3-7E2B-4EE6-AE2C-57902F5A3E94}.Release|Any CPU.Build.0 = Release|Any CPU - {E432067A-D04B-4021-A6A2-4E62EE83139A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E432067A-D04B-4021-A6A2-4E62EE83139A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E432067A-D04B-4021-A6A2-4E62EE83139A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E432067A-D04B-4021-A6A2-4E62EE83139A}.Release|Any CPU.Build.0 = Release|Any CPU {F55592A9-DBCC-4B9A-BA25-205CEC8A641A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F55592A9-DBCC-4B9A-BA25-205CEC8A641A}.Debug|Any CPU.Build.0 = Debug|Any CPU {F55592A9-DBCC-4B9A-BA25-205CEC8A641A}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -91,7 +85,6 @@ Global {396F28A0-999B-4CF1-B7EC-74E5F5063BAA} = {3FD69CA3-BAF7-4B7A-AAB2-B9853759F4D7} {C54ACC90-5597-4C49-96E8-64D15714A1CD} = {115CAC93-F1BB-40FA-A55E-D846ED028024} {DA8A1EF3-7E2B-4EE6-AE2C-57902F5A3E94} = {115CAC93-F1BB-40FA-A55E-D846ED028024} - {E432067A-D04B-4021-A6A2-4E62EE83139A} = {115CAC93-F1BB-40FA-A55E-D846ED028024} {F55592A9-DBCC-4B9A-BA25-205CEC8A641A} = {3FD69CA3-BAF7-4B7A-AAB2-B9853759F4D7} {60AE7517-C5EF-479E-87B7-2BDF2AF45A61} = {1AF242BB-9AA3-469F-83E4-3C1752C9087A} {1AF242BB-9AA3-469F-83E4-3C1752C9087A} = {3FD69CA3-BAF7-4B7A-AAB2-B9853759F4D7} diff --git a/ProjectCommon.targets b/ProjectCommon.targets index 023ac88..7a7a259 100644 --- a/ProjectCommon.targets +++ b/ProjectCommon.targets @@ -1,9 +1,9 @@  - net5.0;netstandard2.1;netstandard2.0;net461 + net6.0;netstandard2.1;netstandard2.0;net461 true $(VersionSuffix) - 0.0.4.5 + 0.0.4.6 $(Version) $(Version) $(Version)-$(VersionSuffix) @@ -22,7 +22,7 @@ False - + diff --git a/src/Norns.Urd.Caching.Abstractions/Norns.Urd.Caching.Abstractions.csproj b/src/Norns.Urd.Caching.Abstractions/Norns.Urd.Caching.Abstractions.csproj index 1ff3e2e..fad4112 100644 --- a/src/Norns.Urd.Caching.Abstractions/Norns.Urd.Caching.Abstractions.csproj +++ b/src/Norns.Urd.Caching.Abstractions/Norns.Urd.Caching.Abstractions.csproj @@ -1,8 +1,8 @@ - - + + diff --git a/src/Norns.Urd.Caching.DistributedCache/Norns.Urd.Caching.DistributedCache.csproj b/src/Norns.Urd.Caching.DistributedCache/Norns.Urd.Caching.DistributedCache.csproj index 4211ee7..0108234 100644 --- a/src/Norns.Urd.Caching.DistributedCache/Norns.Urd.Caching.DistributedCache.csproj +++ b/src/Norns.Urd.Caching.DistributedCache/Norns.Urd.Caching.DistributedCache.csproj @@ -3,7 +3,7 @@ - + diff --git a/src/Norns.Urd.Caching.Memory/Norns.Urd.Caching.Memory.csproj b/src/Norns.Urd.Caching.Memory/Norns.Urd.Caching.Memory.csproj index 818f1da..59bdde3 100644 --- a/src/Norns.Urd.Caching.Memory/Norns.Urd.Caching.Memory.csproj +++ b/src/Norns.Urd.Caching.Memory/Norns.Urd.Caching.Memory.csproj @@ -1,8 +1,8 @@ - - + + diff --git a/src/Norns.Urd.Extensions.Polly/Norns.Urd.Extensions.Polly.csproj b/src/Norns.Urd.Extensions.Polly/Norns.Urd.Extensions.Polly.csproj index b09befe..6a35427 100644 --- a/src/Norns.Urd.Extensions.Polly/Norns.Urd.Extensions.Polly.csproj +++ b/src/Norns.Urd.Extensions.Polly/Norns.Urd.Extensions.Polly.csproj @@ -1,7 +1,7 @@  - + diff --git a/src/Norns.Urd.HttpClient.NewtonsoftJson/Norns.Urd.HttpClient.NewtonsoftJson.csproj b/src/Norns.Urd.HttpClient.NewtonsoftJson/Norns.Urd.HttpClient.NewtonsoftJson.csproj index d8045d8..aa163d9 100644 --- a/src/Norns.Urd.HttpClient.NewtonsoftJson/Norns.Urd.HttpClient.NewtonsoftJson.csproj +++ b/src/Norns.Urd.HttpClient.NewtonsoftJson/Norns.Urd.HttpClient.NewtonsoftJson.csproj @@ -1,11 +1,11 @@ - net5.0;netstandard2.1;netstandard2.0 + net6.0;netstandard2.1;netstandard2.0 Norns.Urd.Http - + diff --git a/src/Norns.Urd.HttpClient/Norns.Urd.HttpClient.csproj b/src/Norns.Urd.HttpClient/Norns.Urd.HttpClient.csproj index 39f546a..9018f76 100644 --- a/src/Norns.Urd.HttpClient/Norns.Urd.HttpClient.csproj +++ b/src/Norns.Urd.HttpClient/Norns.Urd.HttpClient.csproj @@ -1,17 +1,17 @@ - net5.0;netstandard2.1;netstandard2.0 + net6.0;netstandard2.1;netstandard2.0 Norns.Urd.Http - - + + - - - + + + diff --git a/src/Norns.Urd/Linq/EnumerableExtensions.cs b/src/Norns.Urd/Linq/EnumerableExtensions.cs index d98bc66..1a2dd4b 100644 --- a/src/Norns.Urd/Linq/EnumerableExtensions.cs +++ b/src/Norns.Urd/Linq/EnumerableExtensions.cs @@ -4,6 +4,8 @@ namespace System.Linq { public static partial class EnumerableExtensions { +#if NET6_0_OR_GREATER +#else public static IEnumerable DistinctBy(this IEnumerable source, Func getKey) { var keys = new HashSet(); @@ -16,15 +18,6 @@ public static IEnumerable DistinctBy(this IEnumerable Union(this TSource source, IEnumerable sources) - { - yield return source; - foreach (TSource element in sources) - { - yield return element; - } - } - public static TSource FirstOrDefault(this IEnumerable sources, TSource defaultValue) { var enumetrator = sources.GetEnumerator(); @@ -37,6 +30,16 @@ public static TSource FirstOrDefault(this IEnumerable sources, return defaultValue; } } +#endif + + public static IEnumerable Union(this TSource source, IEnumerable sources) + { + yield return source; + foreach (TSource element in sources) + { + yield return element; + } + } public static bool IsNullOrEmpty(this IEnumerable sources) { diff --git a/src/Norns.Urd/Norns.Urd.csproj b/src/Norns.Urd/Norns.Urd.csproj index fd57c8e..04145df 100644 --- a/src/Norns.Urd/Norns.Urd.csproj +++ b/src/Norns.Urd/Norns.Urd.csproj @@ -6,8 +6,8 @@ true - - + + diff --git a/test/Examples.WebApi/Examples.WebApi.csproj b/test/Examples.WebApi/Examples.WebApi.csproj index e5648f8..3b9b092 100644 --- a/test/Examples.WebApi/Examples.WebApi.csproj +++ b/test/Examples.WebApi/Examples.WebApi.csproj @@ -1,7 +1,7 @@ - net5.0 + net6.0 false false diff --git a/test/Norns.Urd.Test/Norns.Urd.Test.csproj b/test/Norns.Urd.Test/Norns.Urd.Test.csproj index e6879b7..d73e3c9 100644 --- a/test/Norns.Urd.Test/Norns.Urd.Test.csproj +++ b/test/Norns.Urd.Test/Norns.Urd.Test.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 false @@ -13,7 +13,7 @@ - + diff --git a/test/SimpleBenchmark/SimpleBenchmark.csproj b/test/SimpleBenchmark/SimpleBenchmark.csproj index 556135c..ee14648 100644 --- a/test/SimpleBenchmark/SimpleBenchmark.csproj +++ b/test/SimpleBenchmark/SimpleBenchmark.csproj @@ -2,14 +2,14 @@ Exe - net5.0 + net6.0 false false - - + + From 5da6141bc2b6fe05fe7e6565a2523da1e78edadf Mon Sep 17 00:00:00 2001 From: "Victor.X.Qu (g-mis.cncd02.Newegg) 42377" Date: Wed, 9 Mar 2022 15:28:31 +0800 Subject: [PATCH 5/5] update --- .github/workflows/build.yml | 2 +- .github/workflows/nuget-prereleased.yml | 2 +- .github/workflows/nuget-released.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index acd87b7..cc20f12 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.100 + dotnet-version: 6.0.201 - name: Install dependencies run: dotnet restore - name: Build diff --git a/.github/workflows/nuget-prereleased.yml b/.github/workflows/nuget-prereleased.yml index 72d9fd8..dd00910 100644 --- a/.github/workflows/nuget-prereleased.yml +++ b/.github/workflows/nuget-prereleased.yml @@ -15,7 +15,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.100 + dotnet-version: 6.0.201 - name: Install dependencies run: dotnet restore - name: Build diff --git a/.github/workflows/nuget-released.yml b/.github/workflows/nuget-released.yml index 8d48993..9272969 100644 --- a/.github/workflows/nuget-released.yml +++ b/.github/workflows/nuget-released.yml @@ -15,7 +15,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.100 + dotnet-version: 6.0.201 - name: Install dependencies run: dotnet restore - name: Build