发布:3.1.3

This commit is contained in:
若汝棋茗
2025-05-18 22:03:57 +08:00
parent 61541f5ff5
commit 0130f0be8b
68 changed files with 883 additions and 724 deletions

View File

@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<BaseVersion>3.1.2</BaseVersion>
<BaseVersion>3.1.3</BaseVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<TouchSocketVersion>$(BaseVersion)</TouchSocketVersion>

View File

@@ -217,7 +217,7 @@ public class WebSocketDmtpService : ConnectableService<WebSocketDmtpSessionClien
/// 尝试添加客户端到集合中。
/// </summary>
/// <param name="sessionClient">WebSocketDmtpSessionClient对象。</param>
/// <returns>如果添加成功返回true否则返回false。</returns>
/// <returns>如果添加成功返回<see langword="true"/>,否则返回<see langword="false"/>。</returns>
internal bool TryAdd(WebSocketDmtpSessionClient sessionClient)
{
return this.m_clients.TryAdd(sessionClient);
@@ -228,7 +228,7 @@ public class WebSocketDmtpService : ConnectableService<WebSocketDmtpSessionClien
/// </summary>
/// <param name="id">客户端标识符。</param>
/// <param name="sessionClient">移除的WebSocketDmtpSessionClient对象。</param>
/// <returns>如果移除成功返回true否则返回false。</returns>
/// <returns>如果移除成功返回<see langword="true"/>,否则返回<see langword="false"/>。</returns>
internal bool TryRemove(string id, out WebSocketDmtpSessionClient sessionClient)
{
return this.m_clients.TryRemoveClient(id, out sessionClient);

View File

@@ -131,7 +131,7 @@ public sealed partial class TouchSocketBitConverter
/// <summary>
/// 判断当前字节序是否与系统字节序相同
/// </summary>
/// <returns>如果字节序相同返回true否则返回false</returns>
/// <returns>如果字节序相同返回<see langword="true"/>,否则返回<see langword="false"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsSameOfSet()
{

View File

@@ -66,7 +66,7 @@ public static class CacheManagementExtensions
/// <param name="key">要获取的缓存键。</param>
/// <param name="value">输出参数,包含获取的缓存值。</param>
/// <param name="update">是否更新缓存项的时间戳。</param>
/// <returns>如果成功获取值则返回true否则返回false。</returns>
/// <returns>如果成功获取值则返回<see langword="true"/>,否则返回<see langword="false"/>。</returns>
public static bool TryGetValue<TKey, TValue>(this ICache<TKey, TValue> cacheClient, TKey key, out TValue value, bool update = false)
{
// 从缓存中获取缓存项。

View File

@@ -10,14 +10,16 @@
// 感谢您的下载和使用
// ------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TouchSocket.Core;
#if NET6_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL2104",
Justification = "The list only contains types stored through the annotated setter111.")]
[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL3153",
Justification = "The list only contains types stored through the annotated setter222.")]
#endif
//[PluginRaise(typeof(ILoadingConfigPlugin))]
//[PluginRaise(typeof(ILoadedConfigPlugin))]
//internal static partial class PluginRaiseExtension
//{
//}

View File

@@ -10,6 +10,8 @@
// 感谢您的下载和使用
//------------------------------------------------------------------------------
using System.Runtime.CompilerServices;
namespace TouchSocket.Core;
/// <summary>
@@ -21,4 +23,18 @@ public static class GlobalEnvironment
/// 动态构建类型默认使用IL
/// </summary>
public static DynamicBuilderType DynamicBuilderType { get; set; } = DynamicBuilderType.IL;
#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
/// <summary>
/// 是否支持动态代码
/// </summary>
public static bool IsDynamicCodeSupported => RuntimeFeature.IsDynamicCodeSupported;
#else
/// <summary>
/// 是否支持动态代码
/// </summary>
public static bool IsDynamicCodeSupported => true;
#endif
}

View File

@@ -20,7 +20,6 @@ namespace TouchSocket.Core;
[FastConverter(typeof(MetadataFastBinaryConverter))]
public sealed class Metadata : Dictionary<string, string>, IPackage
{
/// <summary>
/// 获取或设置指定键的值。
/// </summary>
@@ -28,8 +27,16 @@ public sealed class Metadata : Dictionary<string, string>, IPackage
/// <returns></returns>
public new string this[string key]
{
get => this.TryGetValue(key, out var value) ? value : null;
set => this.Add(key, value);
get
{
ThrowHelper.ThrowArgumentNullExceptionIf(key, nameof(key));
return this.TryGetValue(key, out var value) ? value : null;
}
set
{
ThrowHelper.ThrowArgumentNullExceptionIf(key, nameof(key));
base[key] = value;
}
}
/// <summary>
@@ -40,15 +47,7 @@ public sealed class Metadata : Dictionary<string, string>, IPackage
/// <returns>返回当前元数据对象,以支持链式调用。</returns>
public new Metadata Add(string name, string value)
{
if (this.ContainsKey(name))
{
base[name] = value;
}
else
{
base.Add(name, value);
}
base[name] = value;
return this;
}

View File

@@ -50,7 +50,7 @@ public abstract class CacheDataHandlingAdapter : SingleStreamDataHandlingAdapter
/// <summary>
/// 获取当前缓存,
/// 如果缓存超时或者不存在均会返回false。
/// 如果缓存超时,或者不存在,均会返回<see langword="false"/>
/// 如果获取成功,则会清空内部缓存。
/// </summary>
/// <returns></returns>

View File

@@ -132,7 +132,7 @@ public interface IBigFixedHeaderRequestInfo : IRequestInfo
/// <summary>
/// 当收到数据,由框架封送固定协议头。
/// <para>您需要在此函数中,解析自己的固定包头,并且对<see cref="BodyLength"/>赋值后续数据的长度然后返回True。</para>
/// <para>如果返回false则意味着放弃本次解析</para>
/// <para>如果返回<see langword="false"/>,则意味着放弃本次解析</para>
/// </summary>
/// <param name="header"></param>
/// <returns></returns>

View File

@@ -125,7 +125,7 @@ public interface IBigUnfixedHeaderRequestInfo : IRequestInfo
/// <summary>
/// 当收到数据,由框架封送数据,您需要在此函数中,解析自己的数据包头。
/// <para>如果满足包头的解析请返回True并且递增整个包头的长度到<see cref="ByteBlock.Position"/>,然后赋值<see cref="BodyLength"/></para>
/// <para>如果返回false意味着缓存剩余数据此时如果仅仅是因为长度不足则不必修改其他。</para>
/// <para>如果返回<see langword="false"/>,意味着缓存剩余数据,此时如果仅仅是因为长度不足,则不必修改其他。</para>
/// <para>但是如果是因为数据错误,则需要修改<see cref="ByteBlock.Position"/>到正确位置,如果都不正确,则设置<see cref="ByteBlock.Position"/>等于<see cref="ByteBlock.Length"/></para>
/// </summary>
/// <param name="byteBlock"></param>

View File

@@ -133,7 +133,7 @@ public interface IFixedHeaderByteBlockRequestInfo : IRequestInfo
/// <summary>
/// 当收到数据,由框架封送固定协议头。
/// <para>您需要在此函数中,解析自己的固定包头,并且对<see cref="BodyLength"/>赋值后续数据的长度然后返回True。</para>
/// <para>如果返回false则意味着放弃本次解析</para>
/// <para>如果返回<see langword="false"/>,则意味着放弃本次解析</para>
/// </summary>
/// <param name="header"></param>
/// <returns></returns>
@@ -141,7 +141,7 @@ public interface IFixedHeaderByteBlockRequestInfo : IRequestInfo
/// <summary>
/// 当收到数据,由框架封送有效载荷数据。
/// <para>如果返回false意味着放弃本次解析的所有数据包括已经解析完成的Header</para>
/// <para>如果返回<see langword="false"/>意味着放弃本次解析的所有数据包括已经解析完成的Header</para>
/// </summary>
/// <param name="byteBlock">载荷数据注意该字节块生命周期不受框架控制请一定自行调用Dispose</param>
/// <returns>是否成功有效</returns>

View File

@@ -119,7 +119,7 @@ public interface IFixedHeaderRequestInfo : IRequestInfo
/// <summary>
/// 当收到数据,由框架封送固定协议头。
/// <para>您需要在此函数中,解析自己的固定包头,并且对<see cref="BodyLength"/>赋值后续数据的长度然后返回True。</para>
/// <para>如果返回false则意味着放弃本次解析</para>
/// <para>如果返回<see langword="false"/>,则意味着放弃本次解析</para>
/// </summary>
/// <param name="header"></param>
/// <returns></returns>
@@ -127,7 +127,7 @@ public interface IFixedHeaderRequestInfo : IRequestInfo
/// <summary>
/// 当收到数据,由框架封送有效载荷数据。
/// <para>如果返回false意味着放弃本次解析的所有数据包括已经解析完成的Header</para>
/// <para>如果返回<see langword="false"/>意味着放弃本次解析的所有数据包括已经解析完成的Header</para>
/// </summary>
/// <param name="body">载荷数据</param>
/// <returns>是否成功有效</returns>

View File

@@ -31,7 +31,7 @@ public interface IUnfixedHeaderRequestInfo : IRequestInfo
/// <summary>
/// 当收到数据,由框架封送有效载荷数据。
/// <para>如果返回false意味着放弃本次解析的所有数据包括已经解析完成的Header</para>
/// <para>如果返回<see langword="false"/>意味着放弃本次解析的所有数据包括已经解析完成的Header</para>
/// </summary>
/// <param name="body">载荷数据</param>
/// <returns>是否成功有效</returns>
@@ -40,7 +40,7 @@ public interface IUnfixedHeaderRequestInfo : IRequestInfo
/// <summary>
/// 当收到数据,由框架封送数据,您需要在此函数中,解析自己的数据包头。
/// <para>如果满足包头的解析请返回True并且递增整个包头的长度到<see cref="ByteBlock.Position"/>,然后赋值<see cref="BodyLength"/></para>
/// <para>如果返回false意味着缓存剩余数据此时如果仅仅是因为长度不足则不必修改其他。</para>
/// <para>如果返回<see langword="false"/>,意味着缓存剩余数据,此时如果仅仅是因为长度不足,则不必修改其他。</para>
/// <para>但是如果是因为数据错误,则需要修改<see cref="ByteBlock.Position"/>到正确位置,如果都不正确,则设置<see cref="ByteBlock.Position"/>等于<see cref="ByteBlock.Length"/></para>
/// </summary>
/// <param name="byteBlock"></param>

View File

@@ -69,10 +69,10 @@ public static class CollectionsExtension
/// <param name="dictionary">要添加键值对的字典。</param>
/// <param name="key">要添加的键。</param>
/// <param name="value">要添加的值。</param>
/// <returns>如果添加成功则返回true否则返回false。</returns>
/// <returns>如果添加成功则返回<see langword="true"/>,否则返回<see langword="false"/>。</returns>
public static bool TryAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
{
// 如果字典中已经包含此键则不添加并返回false
// 如果字典中已经包含此键,则不添加,并返回<see langword="false"/>
if (dictionary.ContainsKey(key))
{
return false;
@@ -97,11 +97,11 @@ public static class CollectionsExtension
{
if (dictionary.TryGetValue(key,out value))
{
// 如果字典中包含此键则移除并返回true
// 如果字典中包含此键,则移除并返回<see langword="true"/>
dictionary.Remove(key);
return true;
}
// 如果字典中不包含此键则返回false
// 如果字典中不包含此键,则返回<see langword="false"/>
value = default;
return false;
}

View File

@@ -39,11 +39,11 @@ public static class ReflectionExtension
private static string GenerateKey(MethodInfo method)
{
var parameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray(); // 使用类型名称
var parameterTypes = method.GetParameters().Select(p => p.ParameterType); // 使用类型名称
return GenerateKey(method.Name, parameterTypes);
}
private static string GenerateKey(string methodName, Type[] parameterTypes)
private static string GenerateKey(string methodName, IEnumerable<Type> parameterTypes)
{
// 将参数类型名称转换为合法的标识符
var parameterTypeNames = string.Join("_", parameterTypes.Select(t => StringExtension.MakeIdentifier(t.GetRefOutType().Name)));
@@ -145,28 +145,28 @@ public static class ReflectionExtension
/// 检查属性是否可以公开写入
/// </summary>
/// <param name="propertyInfo">要检查的属性信息</param>
/// <returns>如果属性可以公开写入则返回true否则返回false</returns>
/// <returns>如果属性可以公开写入则返回<see langword="true"/>,否则返回<see langword="false"/></returns>
public static bool CanPublicWrite(this PropertyInfo propertyInfo)
{
// 如果属性不支持写操作则直接返回false
// 如果属性不支持写操作,则直接返回<see langword="false"/>
if (!propertyInfo.CanWrite)
{
return false;
}
// 如果属性的设置方法为null则表示不能写入返回false
// 如果属性的设置方法为null则表示不能写入返回<see langword="false"/>
if (propertyInfo.SetMethod == null)
{
return false;
}
// 如果属性的设置方法是公共的并且接受一个参数则认为该属性可以公开写入返回true
// 如果属性的设置方法是公共的,并且接受一个参数,则认为该属性可以公开写入,返回<see langword="true"/>
if (propertyInfo.SetMethod.IsPublic && propertyInfo.SetMethod.GetParameters().Length == 1)
{
return true;
}
// 如果上述条件都不满足则返回false
// 如果上述条件都不满足,则返回<see langword="false"/>
return false;
}

View File

@@ -107,7 +107,7 @@ public static class StringExtension
/// 检查字符串是否具有有效值。
/// </summary>
/// <param name="str">要检查的字符串。</param>
/// <returns>如果字符串不是null且非空格或制表符等则返回true否则返回false。</returns>
/// <returns>如果字符串不是null且非空格或制表符等则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
public static bool HasValue([NotNullWhen(true)] this string str)
{
// 使用string.IsNullOrWhiteSpace方法检查字符串是否为null或包含仅空格或制表符等
@@ -311,10 +311,10 @@ public static class StringExtension
///<param name="value">待解析的字符串</param>
///<param name="destinationType">目标数据类型</param>
/// <param name="returnValue">解析后的值,输出参数</param>
/// <returns>如果解析成功返回true否则返回false</returns>
/// <returns>如果解析成功返回<see langword="true"/>,否则返回<see langword="false"/></returns>
public static bool TryParseToType(string value, Type destinationType, out object returnValue)
{
// 如果字符串为空或只含空格将returnValue设为默认值并返回true
// 如果字符串为空或只含空格将returnValue设为默认值并返回<see langword="true"/>
if (string.IsNullOrEmpty(value))
{
returnValue = default;
@@ -421,7 +421,7 @@ public static class StringExtension
returnValue = value;
return true;
}
// 对空类型、对象类型或数据库空值类型将returnValue设为默认值并返回false
// 对空类型、对象类型或数据库空值类型将returnValue设为默认值并返回<see langword="false"/>
case TypeCode.Empty:
case TypeCode.Object:
case TypeCode.DBNull:

View File

@@ -23,6 +23,7 @@ using System.Text;
using System.Threading;
using System.Buffers;
using System.Threading.Tasks;
using System.Linq;
namespace TouchSocket.Core;
@@ -206,7 +207,7 @@ public static class SystemExtension
/// </summary>
/// <param name="b">要检查的无符号长整型数值。</param>
/// <param name="index">要检查的位的位置从0到63。</param>
/// <returns>如果指定位置的位为1则返回true否则返回false。</returns>
/// <returns>如果指定位置的位为1则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
/// <exception cref="ArgumentOutOfRangeException">当索引值不在0到63之间时抛出此异常。</exception>
public static bool GetBit(this ulong b, int index)
{
@@ -222,7 +223,7 @@ public static class SystemExtension
/// </summary>
/// <param name="b">要检查的无符号整型数值。</param>
/// <param name="index">要检查的位的位置从0到31。</param>
/// <returns>如果指定位置的位为1则返回true否则返回false。</returns>
/// <returns>如果指定位置的位为1则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
/// <exception cref="ArgumentOutOfRangeException">当索引值不在0到31之间时抛出此异常。</exception>
public static bool GetBit(this uint b, int index)
{
@@ -238,7 +239,7 @@ public static class SystemExtension
/// </summary>
/// <param name="b">要检查的无符号短整型数值。</param>
/// <param name="index">要检查的位的位置从0到15。</param>
/// <returns>如果指定位置的位为1则返回true否则返回false。</returns>
/// <returns>如果指定位置的位为1则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
/// <exception cref="ArgumentOutOfRangeException">当索引值不在0到15之间时抛出此异常。</exception>
public static bool GetBit(this ushort b, int index)
{
@@ -254,7 +255,7 @@ public static class SystemExtension
/// </summary>
/// <param name="b">要检查的字节型数值。</param>
/// <param name="index">要检查的位的位置从0到7。</param>
/// <returns>如果指定位置的位为1则返回true否则返回false。</returns>
/// <returns>如果指定位置的位为1则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
/// <exception cref="ArgumentOutOfRangeException">当索引值不在0到7之间时抛出此异常。</exception>
public static bool GetBit(this byte b, int index)
{
@@ -632,6 +633,39 @@ public static class SystemExtension
return type.IsPrimitive || type == typeof(string);
}
/// <summary>
/// 获取类型的短确定性名称,支持泛型。
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static string GetDeterminantName(this Type type)
{
IEnumerable<Type> types;
if (type.IsGenericType)
{
types = type.GetGenericArguments();
}
else if (type.IsArray)
{
types = new Type[] { type.GetElementType() };
}
else
{
types = [];
}
var stringBuilder = new StringBuilder();
stringBuilder.Append(type.Namespace);
stringBuilder.Append(type.Name);
foreach (var item in types)
{
stringBuilder.Append(item.Namespace);
stringBuilder.Append(item.Name);
}
return stringBuilder.ToString();
}
#endregion Type
#region Memory

View File

@@ -60,7 +60,7 @@ public class FlowGate : Counter
/// </remarks>
public async Task AddCheckWaitAsync(long increment)
{
// 尝试增加计数器如果返回true则表示增加成功需要进一步处理
// 尝试增加计数器,如果返回<see langword="true"/>,则表示增加成功,需要进一步处理
if (this.Increment(increment))
{
// 如果当前计数超过设定的最大值

View File

@@ -377,7 +377,7 @@ public interface IByteBlock : IDisposable, IBufferWriter<byte>
/// <summary>
/// 读取是否为null。
/// </summary>
/// <returns>如果读取内容为null则返回true否则返回false。</returns>
/// <returns>如果读取内容为null则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
bool ReadIsNull();
/// <summary>

View File

@@ -0,0 +1,130 @@
// ------------------------------------------------------------------------------
// 此代码版权除特别声明或在XREF结尾的命名空间的代码归作者本人若汝棋茗所有
// 源代码使用协议遵循本仓库的开源协议及附加协议若本仓库没有设置则按MIT开源协议授权
// CSDN博客https://blog.csdn.net/qq_40374647
// 哔哩哔哩视频https://space.bilibili.com/94253567
// Gitee源代码仓库https://gitee.com/RRQM_Home
// Github源代码仓库https://github.com/RRQM
// API首页https://touchsocket.net/
// 交流QQ群234762506
// 感谢您的下载和使用
// ------------------------------------------------------------------------------
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace TouchSocket.Core;
abstract class DynamicMethodInfoBase : IDynamicMethodInfo
{
public DynamicMethodInfoBase(MethodInfo method)
{
if (method.ReturnType == typeof(void))
{
this.ReturnKind = MethodReturnKind.Void;
}
else if (IsTypeAwaitable(method.ReturnType, out var returnType))
{
if (returnType is null)
{
this.ReturnKind = MethodReturnKind.Awaitable;
}
else
{
this.RealReturnType = returnType;
this.ReturnKind = MethodReturnKind.AwaitableObject;
}
}
else if (method.ReturnType == typeof(Task)|| method.ReturnType == typeof(ValueTask))
{
this.ReturnKind = MethodReturnKind.Awaitable;
}
else if (method.ReturnType.IsGenericType && (method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)|| method.ReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>)))
{
this.RealReturnType = method.ReturnType.GetGenericArguments().First();
this.ReturnKind = MethodReturnKind.AwaitableObject;
}
else
{
this.RealReturnType = method.ReturnType;
this.ReturnKind = MethodReturnKind.Object;
}
}
public Type RealReturnType { get; set; }
public MethodReturnKind ReturnKind { get; set; }
public async Task<object> GetResultAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] object result)
{
if (result is Task task)
{
await task.ConfigureAwait(EasyTask.ContinueOnCapturedContext);
if (this.ReturnKind== MethodReturnKind.AwaitableObject)
{
return DynamicMethodMemberAccessor.Default.GetValue(task, nameof(Task<object>.Result));
}
return null;
}
ThrowHelper.ThrowException("当源生成无法使用时无法处理非Task的Awaitable对象。");
return null;
}
public abstract object Invoke(object instance, object[] parameters);
private static bool IsTypeAwaitable([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type, out Type returnType)
{
returnType = null;
// 1. 查找GetAwaiter实例方法无参数
MethodInfo getAwaiterMethod = type.GetMethod("GetAwaiter", BindingFlags.Public | BindingFlags.Instance);
if (getAwaiterMethod == null)
{
return false;
}
// 2. 获取Awaiter类型
Type awaiterType = getAwaiterMethod.ReturnType;
// 3. 验证Awaiter是否实现必要的接口
Type inotifyCompletion = typeof(System.Runtime.CompilerServices.INotifyCompletion);
Type icriticalNotifyCompletion = typeof(System.Runtime.CompilerServices.ICriticalNotifyCompletion);
var implementsInterface = awaiterType.GetInterfaces().Any(i =>
i == inotifyCompletion || i == icriticalNotifyCompletion);
if (!implementsInterface)
{
return false;
}
// 4. 检查IsCompleted属性
var isCompletedProp = awaiterType.GetProperty("IsCompleted", BindingFlags.Public | BindingFlags.Instance);
if (isCompletedProp == null ||
isCompletedProp.PropertyType != typeof(bool) ||
!isCompletedProp.CanRead)
{
return false;
}
// 5. 检查GetResult方法
var getResultMethod = awaiterType.GetMethod("GetResult", BindingFlags.Public | BindingFlags.Instance);
if (getResultMethod == null)
{
return false;
}
// 6. 检查GetResult方法的返回类型
if (getResultMethod.ReturnType == typeof(void))
{
returnType = null;
}
else
{
returnType = getResultMethod.ReturnType;
}
return true;
}
}

View File

@@ -12,6 +12,7 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
namespace TouchSocket.Core;
@@ -44,23 +45,23 @@ public class DynamicMethodMemberAccessor : IMemberAccessor
public Func<Type, PropertyInfo[]> OnGetProperties { get; set; }
/// <inheritdoc/>
public object GetValue(object instance, string memberName)
public object GetValue([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] object instance, string memberName)
{
return this.FindClassAccessor(instance).GetValue(instance, memberName);
}
/// <inheritdoc/>
public void SetValue(object instance, string memberName, object newValue)
public void SetValue([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] object instance, string memberName, object newValue)
{
this.FindClassAccessor(instance).SetValue(instance, memberName, newValue);
}
private IMemberAccessor FindClassAccessor(object instance)
private IMemberAccessor FindClassAccessor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] object instance)
{
var typekey = instance.GetType();
if (!this.m_classAccessors.TryGetValue(typekey, out var classAccessor))
var typeKey = instance.GetType();
if (!this.m_classAccessors.TryGetValue(typeKey, out var classAccessor))
{
var memberAccessor = new MemberAccessor(instance.GetType());
var memberAccessor = new MemberAccessor(typeKey);
if (this.OnGetFieldInfes != null)
{
memberAccessor.OnGetFieldInfos = this.OnGetFieldInfes;
@@ -72,7 +73,7 @@ public class DynamicMethodMemberAccessor : IMemberAccessor
}
memberAccessor.Build();
classAccessor = memberAccessor;
this.m_classAccessors.TryAdd(typekey, classAccessor);
this.m_classAccessors.TryAdd(typeKey, classAccessor);
}
return classAccessor;
}

View File

@@ -0,0 +1,74 @@
// ------------------------------------------------------------------------------
// 此代码版权除特别声明或在XREF结尾的命名空间的代码归作者本人若汝棋茗所有
// 源代码使用协议遵循本仓库的开源协议及附加协议若本仓库没有设置则按MIT开源协议授权
// CSDN博客https://blog.csdn.net/qq_40374647
// 哔哩哔哩视频https://space.bilibili.com/94253567
// Gitee源代码仓库https://gitee.com/RRQM_Home
// Github源代码仓库https://github.com/RRQM
// API首页https://touchsocket.net/
// 交流QQ群234762506
// 感谢您的下载和使用
// ------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace TouchSocket.Core;
internal class ExpressionDynamicMethodInfo : DynamicMethodInfoBase
{
private Func<object, object[], object> m_func;
public ExpressionDynamicMethodInfo(MethodInfo method) : base(method)
{
m_func = CreateExpressionInvoker(method);
}
public override object Invoke(object instance, object[] parameters)
{
return m_func.Invoke(instance, parameters);
}
/// <summary>
/// 构建表达式树调用
/// </summary>
/// <param name="method"></param>
/// <returns></returns>
protected Func<object, object[], object> CreateExpressionInvoker(MethodInfo method)
{
var instance = Expression.Parameter(typeof(object), "instance");
var parameters = Expression.Parameter(typeof(object[]), "parameters");
var instanceCast = method.IsStatic ? null : Expression.Convert(instance, method.DeclaringType);
var parametersCast = method.GetParameters().Select((p, i) =>
{
var parameter = Expression.ArrayIndex(parameters, Expression.Constant(i));
return Expression.Convert(parameter, p.ParameterType);
});
var body = Expression.Call(instanceCast, method, parametersCast);
switch (this.ReturnKind)
{
case MethodReturnKind.Void:
{
var action = Expression.Lambda<Action<object, object[]>>(body, instance, parameters).Compile();
return (_instance, _parameters) =>
{
action.Invoke(_instance, _parameters);
return null;
};
}
default:
{
var bodyCast = Expression.Convert(body, typeof(object));
return Expression.Lambda<Func<object, object[], object>>(bodyCast, instance, parameters).Compile();
}
}
}
}

View File

@@ -0,0 +1,36 @@
// ------------------------------------------------------------------------------
// 此代码版权除特别声明或在XREF结尾的命名空间的代码归作者本人若汝棋茗所有
// 源代码使用协议遵循本仓库的开源协议及附加协议若本仓库没有设置则按MIT开源协议授权
// CSDN博客https://blog.csdn.net/qq_40374647
// 哔哩哔哩视频https://space.bilibili.com/94253567
// Gitee源代码仓库https://gitee.com/RRQM_Home
// Github源代码仓库https://github.com/RRQM
// API首页https://touchsocket.net/
// 交流QQ群234762506
// 感谢您的下载和使用
// ------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TouchSocket.Core;
public interface IDynamicMethodInfo
{
/// <summary>
/// 真实返回值类型。
/// <para>当方法为void或task时为null</para>
/// <para>当方法为task泛型时为泛型元素类型</para>
/// </summary>
Type RealReturnType { get;}
/// <summary>
/// 返回值的Task类型。
/// </summary>
MethodReturnKind ReturnKind { get;}
object Invoke(object instance, object[] parameters);
Task<object> GetResultAsync(object result);
}

View File

@@ -0,0 +1,190 @@
// ------------------------------------------------------------------------------
// 此代码版权除特别声明或在XREF结尾的命名空间的代码归作者本人若汝棋茗所有
// 源代码使用协议遵循本仓库的开源协议及附加协议若本仓库没有设置则按MIT开源协议授权
// CSDN博客https://blog.csdn.net/qq_40374647
// 哔哩哔哩视频https://space.bilibili.com/94253567
// Gitee源代码仓库https://gitee.com/RRQM_Home
// Github源代码仓库https://github.com/RRQM
// API首页https://touchsocket.net/
// 交流QQ群234762506
// 感谢您的下载和使用
// ------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
namespace TouchSocket.Core;
internal class ILDynamicMethodInfo : DynamicMethodInfoBase
{
private Func<object, object[], object> m_func;
public ILDynamicMethodInfo(MethodInfo method) : base(method)
{
m_func = CreateILInvoker(method);
}
public override object Invoke(object instance, object[] parameters)
{
return m_func(instance, parameters);
}
protected static Func<object, object[], object> CreateILInvoker(MethodInfo methodInfo)
{
var dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[])
}, methodInfo.DeclaringType.Module);
var il = dynamicMethod.GetILGenerator();
var ps = methodInfo.GetParameters();
var paramTypes = new Type[ps.Length];
for (var i = 0; i < paramTypes.Length; i++)
{
paramTypes[i] = ps[i].ParameterType.IsByRef ? ps[i].ParameterType.GetElementType() : ps[i].ParameterType;
}
var locals = new LocalBuilder[paramTypes.Length];
for (var i = 0; i < paramTypes.Length; i++)
{
locals[i] = il.DeclareLocal(paramTypes[i], true);
}
for (var i = 0; i < paramTypes.Length; i++)
{
il.Emit(OpCodes.Ldarg_1);
EmitFastInt(il, i);
il.Emit(OpCodes.Ldelem_Ref);
EmitCastToReference(il, paramTypes[i]);
il.Emit(OpCodes.Stloc, locals[i]);
}
if (!methodInfo.IsStatic)
{
il.Emit(OpCodes.Ldarg_0);
}
for (var i = 0; i < paramTypes.Length; i++)
{
if (ps[i].ParameterType.IsByRef)
{
il.Emit(OpCodes.Ldloca_S, locals[i]);
}
else
{
il.Emit(OpCodes.Ldloc, locals[i]);
}
}
if (methodInfo.IsStatic)
{
il.EmitCall(OpCodes.Call, methodInfo, null);
}
else
{
il.EmitCall(OpCodes.Callvirt, methodInfo, null);
}
if (methodInfo.ReturnType == typeof(void))
{
il.Emit(OpCodes.Ldnull);
}
else
{
EmitBoxIfNeeded(il, methodInfo.ReturnType);
}
for (var i = 0; i < paramTypes.Length; i++)
{
if (ps[i].ParameterType.IsByRef)
{
il.Emit(OpCodes.Ldarg_1);
EmitFastInt(il, i);
il.Emit(OpCodes.Ldloc, locals[i]);
if (locals[i].LocalType.IsValueType)
{
il.Emit(OpCodes.Box, locals[i].LocalType);
}
il.Emit(OpCodes.Stelem_Ref);
}
}
il.Emit(OpCodes.Ret);
var invoker = (Func<object, object[], object>)dynamicMethod.CreateDelegate(typeof(Func<object, object[], object>));
return invoker;
}
private static void EmitBoxIfNeeded(ILGenerator il, System.Type type)
{
if (type.IsValueType)
{
il.Emit(OpCodes.Box, type);
}
}
private static void EmitCastToReference(ILGenerator il, System.Type type)
{
if (type.IsValueType)
{
il.Emit(OpCodes.Unbox_Any, type);
}
else
{
il.Emit(OpCodes.Castclass, type);
}
}
private static void EmitFastInt(ILGenerator il, int value)
{
switch (value)
{
case -1:
il.Emit(OpCodes.Ldc_I4_M1);
return;
case 0:
il.Emit(OpCodes.Ldc_I4_0);
return;
case 1:
il.Emit(OpCodes.Ldc_I4_1);
return;
case 2:
il.Emit(OpCodes.Ldc_I4_2);
return;
case 3:
il.Emit(OpCodes.Ldc_I4_3);
return;
case 4:
il.Emit(OpCodes.Ldc_I4_4);
return;
case 5:
il.Emit(OpCodes.Ldc_I4_5);
return;
case 6:
il.Emit(OpCodes.Ldc_I4_6);
return;
case 7:
il.Emit(OpCodes.Ldc_I4_7);
return;
case 8:
il.Emit(OpCodes.Ldc_I4_8);
return;
}
if (value > -129 && value < 128)
{
il.Emit(OpCodes.Ldc_I4_S, (SByte)value);
}
else
{
il.Emit(OpCodes.Ldc_I4, value);
}
}
}

View File

@@ -47,7 +47,7 @@ public class MemberAccessor : IMemberAccessor
/// 动态成员访问器
/// </summary>
/// <param name="type"></param>
public MemberAccessor(Type type)
public MemberAccessor([DynamicallyAccessedMembers( DynamicallyAccessedMemberTypes.PublicFields| DynamicallyAccessedMemberTypes.PublicProperties)]Type type)
{
this.Type = type;
this.OnGetFieldInfos = (t) => { return t.GetFields(); };

View File

@@ -16,171 +16,103 @@ using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace TouchSocket.Core;
/// <summary>
/// 一个动态调用方法
/// 动态方法调用器。
/// </summary>
public class Method
{
private const string GeneratorTypeNamespace = "TouchSocket.Core.__Internals";
private readonly IDynamicMethodInfo m_dynamicMethodInfo;
private readonly MethodInfo m_info;
private Func<object, object[], object> m_invoker;
private const string m_generatorTypeNamespace = "TouchSocket.Core.__Internals";
/// <summary>
/// 初始化一个动态调用方法
/// 使用指定的方法信息和动态方法信息初始化<see cref="Method"/>类的新实例。
/// </summary>
/// <param name="method">方法信息</param>
/// <param name="dynamicBuilderType">指定构建的类型</param>
/// <param name="method">关于要表示的方法的元数据信息。不可能 <see langword="null"/>.</param>
/// <param name="dynamicMethodInfo">与该方法相关联的动态方法信息。不可能 <see langword="null"/>.</param>
public Method(MethodInfo method, IDynamicMethodInfo dynamicMethodInfo)
{
ThrowHelper.ThrowArgumentNullExceptionIf(dynamicMethodInfo, nameof(dynamicMethodInfo));
this.m_info = ThrowHelper.ThrowArgumentNullExceptionIf(method, nameof(method));
this.m_dynamicMethodInfo = dynamicMethodInfo;
}
/// <summary>
/// 构造方法,初始化 Method 实例。
/// </summary>
/// <param name="method">目标方法信息。</param>
/// <param name="dynamicBuilderType">指定动态构建类型。</param>
public Method(MethodInfo method, DynamicBuilderType? dynamicBuilderType = default)
{
this.m_info = ThrowHelper.ThrowArgumentNullExceptionIf(method, nameof(method));
this.Name = method.Name;
if (method.ReturnType == typeof(Task))
{
this.HasReturn = false;
this.TaskType = TaskReturnType.Task;
}
else if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
{
this.HasReturn = true;
this.ReturnType = method.ReturnType.GetGenericArguments()[0];
this.TaskType = TaskReturnType.TaskObject;
}
else if (method.ReturnType == typeof(void))
{
this.HasReturn = false;
this.TaskType = TaskReturnType.None;
}
else
{
this.HasReturn = true;
this.TaskType = TaskReturnType.None;
this.ReturnType = method.ReturnType;
}
if (dynamicBuilderType.HasValue)
{
switch (dynamicBuilderType.Value)
{
case DynamicBuilderType.IL:
if (!this.CreateInvokeFromIL())
{
ThrowHelper.ThrowNotSupportedException($"当前环境不支持{dynamicBuilderType.Value}");
if (!GlobalEnvironment.IsDynamicCodeSupported)
{
ThrowHelper.ThrowNotSupportedException($"当前环境不支持{dynamicBuilderType.Value}");
}
this.m_dynamicMethodInfo = new ILDynamicMethodInfo(method);
break;
}
break;
case DynamicBuilderType.Expression:
if (!this.CreateInvokeFromExpression())
{
ThrowHelper.ThrowNotSupportedException($"当前环境不支持{dynamicBuilderType.Value}");
}
this.m_dynamicMethodInfo = new ExpressionDynamicMethodInfo(method);
break;
case DynamicBuilderType.Reflect:
this.m_dynamicMethodInfo = new ReflectDynamicMethodInfo(method);
break;
case DynamicBuilderType.SourceGenerator:
if (!this.CreateInvokeFromSG())
{
ThrowHelper.ThrowNotSupportedException($"当前环境不支持{dynamicBuilderType.Value}");
}
this.m_dynamicMethodInfo = CreateDynamicMethodInfoFromSG();
break;
default:
break;
}
}
else
{
//RuntimeFeature
this.CreateInvokeFromSG();
this.CreateInvokeFromExpression();
this.CreateInvokeFromIL();
this.DynamicBuilderType = dynamicBuilderType.Value;
return;
}
if (this.m_invoker == null)
this.m_dynamicMethodInfo = this.CreateDynamicMethodInfoFromSG();
if (this.m_dynamicMethodInfo != null)
{
this.DynamicBuilderType = DynamicBuilderType.Reflect;
this.DynamicBuilderType = DynamicBuilderType.SourceGenerator;
return;
}
try
{
this.m_dynamicMethodInfo = new ExpressionDynamicMethodInfo(method);
this.DynamicBuilderType = DynamicBuilderType.Expression;
return;
}
catch
{
}
this.m_dynamicMethodInfo = new ReflectDynamicMethodInfo(method);
this.DynamicBuilderType = DynamicBuilderType.Reflect;
}
/// <summary>
/// 初始化一个动态调用方法
/// 初始化一个动态调用方法
/// </summary>
/// <param name="targetType">目标类型</param>
/// <param name="methodName">目标方法</param>
/// <param name="dynamicBuilderType">指定构建的类型</param>
public Method([DynamicallyAccessedMembers( DynamicallyAccessedMemberTypes.PublicMethods)] Type targetType,string methodName, DynamicBuilderType? dynamicBuilderType = default)
:this(targetType.GetMethod(methodName),dynamicBuilderType)
/// <param name="targetType">目标类型</param>
/// <param name="methodName">目标方法名。</param>
/// <param name="dynamicBuilderType">指定构建的类型</param>
public Method([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type targetType, string methodName, DynamicBuilderType? dynamicBuilderType = default)
: this(targetType.GetMethod(methodName), dynamicBuilderType)
{
}
private bool CreateInvokeFromIL()
{
if (this.m_invoker != null)
{
return false;
}
try
{
this.m_invoker = CreateILInvoker(this.Info);
this.DynamicBuilderType = DynamicBuilderType.IL;
return true;
}
catch
{
return false;
}
}
private bool CreateInvokeFromExpression()
{
if (this.m_invoker != null)
{
return false;
}
try
{
this.m_invoker = this.CreateExpressionInvoker(this.Info);
this.DynamicBuilderType = DynamicBuilderType.Expression;
return true;
}
catch
{
return false;
}
}
private bool CreateInvokeFromSG()
{
if (this.m_invoker != null)
{
return false;
}
var typeName = $"{m_generatorTypeNamespace}.__{StringExtension.MakeIdentifier(this.Info.DeclaringType.FullName)}MethodExtension";
var type = this.Info.DeclaringType.Assembly.GetType(typeName);
if (type == null)
{
return false;
}
var methodName = $"{this.Info.GetDeterminantName()}Func";
var property = type.GetProperty(methodName, BindingFlags.Public | BindingFlags.Static);
if (property == null)
{
return false;
}
this.m_invoker = (Func<object, object[], object>)property.GetValue(null);
if (this.m_invoker == null)
{
return false;
}
this.DynamicBuilderType = DynamicBuilderType.SourceGenerator;
return true;
}
/// <summary>
@@ -191,382 +123,117 @@ public class Method
/// <summary>
/// 是否具有返回值。当返回值为Task时也会认为没有返回值。
/// </summary>
public bool HasReturn { get; private set; }
public bool HasReturn => this.RealReturnType!=null;
/// <summary>
/// 方法信息
/// 方法信息
/// </summary>
public MethodInfo Info => this.m_info;
/// <summary>
/// 获取方法名
/// 获取一个值,该值指示该方法的返回类型是否支持等待。
/// </summary>
public string Name { get; protected set; }
public bool IsAwaitable => this.ReturnKind == MethodReturnKind.Awaitable || this.ReturnKind == MethodReturnKind.AwaitableObject;
/// <summary>
/// 返回值类型
/// <para>当方法为void或task时为null</para>
/// <para>当方法为task泛型时为泛型元素类型</para>
/// 获取方法名
/// </summary>
public Type ReturnType { get; private set; }
public string Name => this.m_info.Name;
/// <summary>
/// 真实返回值类型。
/// <para>当方法为void或task时为null。</para>
/// <para>当方法为task泛型时为泛型元素类型。</para>
/// </summary>
public Type RealReturnType => this.m_dynamicMethodInfo.RealReturnType;
/// <summary>
/// 返回值的Task类型。
/// </summary>
public TaskReturnType TaskType { get; private set; }
public MethodReturnKind ReturnKind => this.m_dynamicMethodInfo.ReturnKind;
#region Invoke
/// <summary>
/// 执行方法。
/// 同步调用方法。
/// </summary>
/// <param name="instance">实例</param>
/// <param name="parameters">参数</param>
/// <returns></returns>
/// <param name="instance">实例对象。</param>
/// <param name="parameters">参数数组。</param>
/// <returns>方法返回值。</returns>
public object Invoke(object instance, params object[] parameters)
{
if (this.m_invoker == null)
{
return this.Info.Invoke(instance, parameters);
}
else
{
return this.m_invoker.Invoke(instance, parameters);
}
return this.m_dynamicMethodInfo.Invoke(instance, parameters);
}
/// <summary>
/// 异步执行方法
/// 异步调用方法,返回指定类型结果
/// </summary>
/// <param name="instance">实例</param>
/// <param name="parameters">参数</param>
/// <returns>返回一个表示异步操作的任务。</returns>
public Task InvokeAsync(object instance, params object[] parameters)
/// <typeparam name="TResult">结果类型。</typeparam>
/// <param name="instance">实例对象。</param>
/// <param name="parameters">参数数组。</param>
/// <returns>异步任务,包含方法返回值。</returns>
public async Task<TResult> InvokeAsync<TResult>(object instance, params object[] parameters)
{
switch (this.TaskType)
{
case TaskReturnType.None:
{
throw new Exception("该方法不包含Task。");
}
case TaskReturnType.Task:
{
object re;
if (this.m_invoker == null)
{
re = this.Info.Invoke(instance, parameters);
}
else
{
re = this.m_invoker.Invoke(instance, parameters);
}
return (TResult)await this.InvokeAsync(instance, parameters).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
}
return (Task)re;
}
case TaskReturnType.TaskObject:
/// <summary>
/// 异步调用方法。
/// </summary>
/// <param name="instance">实例对象。</param>
/// <param name="parameters">参数数组。</param>
/// <returns>异步任务,包含方法返回值。</returns>
public async Task<object> InvokeAsync(object instance, params object[] parameters)
{
switch (this.ReturnKind)
{
case MethodReturnKind.Void:
this.Invoke(instance, parameters);
return default;
case MethodReturnKind.Object:
return this.Invoke(instance, parameters);
case MethodReturnKind.Awaitable:
{
object re;
if (this.m_invoker == null)
{
re = this.Info.Invoke(instance, parameters);
}
else
{
re = this.m_invoker.Invoke(instance, parameters);
}
return (Task)re;
var rawResult = this.Invoke(instance, parameters);
await this.m_dynamicMethodInfo.GetResultAsync(rawResult).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
return default;
}
case MethodReturnKind.AwaitableObject:
{
var rawResult = this.Invoke(instance, parameters);
return (await this.m_dynamicMethodInfo.GetResultAsync(rawResult).ConfigureAwait(EasyTask.ContinueOnCapturedContext));
}
default:
return default;
}
}
/// <summary>
/// 调用异步结果
/// </summary>
/// <param name="instance"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public Task<TResult> InvokeAsync<TResult>(object instance, params object[] parameters)
{
switch (this.TaskType)
{
case TaskReturnType.None:
{
throw new Exception($"{this.Info}不包含任何Task<>返回值。他可能是个同步函数。");
}
case TaskReturnType.Task:
{
throw new Exception($"{this.Info}不包含任何可等待的Task<>返回值。");
}
case TaskReturnType.TaskObject:
{
object re;
if (this.m_invoker == null)
{
re = this.Info.Invoke(instance, parameters);
}
else
{
re = this.m_invoker.Invoke(instance, parameters);
}
return (Task<TResult>)re;
}
default:
return default;
}
}
/// <summary>
/// 执行方法。
/// <para>当方法为void或task时会异常</para>
/// <para>当方法为task泛型时会await后的值</para>
/// <para>支持调用方为UI主线程。</para>
/// </summary>
/// <param name="instance">实例</param>
/// <param name="parameters">参数</param>
/// <returns></returns>
public async Task<object> InvokeObjectAsync(object instance, params object[] parameters)
{
switch (this.TaskType)
{
case TaskReturnType.None:
{
throw new Exception($"{this.Info}不包含任何Task<>返回值。他可能是个同步函数。");
}
case TaskReturnType.Task:
{
throw new Exception($"{this.Info}不包含任何可等待的Task<>返回值。");
}
case TaskReturnType.TaskObject:
{
Task task;
if (this.m_invoker == null)
{
task = (Task)this.Info.Invoke(instance, parameters);
}
else
{
task = (Task)this.m_invoker.Invoke(instance, parameters);
}
await task.ConfigureAwait(EasyTask.ContinueOnCapturedContext);
return DynamicMethodMemberAccessor.Default.GetValue(task, "Result");
}
default:
return default;
}
}
/// <summary>
/// 构建IL调用
/// </summary>
/// <param name="methodInfo"></param>
/// <returns></returns>
protected static Func<object, object[], object> CreateILInvoker(MethodInfo methodInfo)
{
var dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[])
}, methodInfo.DeclaringType.Module);
var il = dynamicMethod.GetILGenerator();
var ps = methodInfo.GetParameters();
var paramTypes = new Type[ps.Length];
for (var i = 0; i < paramTypes.Length; i++)
{
paramTypes[i] = ps[i].ParameterType.IsByRef ? ps[i].ParameterType.GetElementType() : ps[i].ParameterType;
}
var locals = new LocalBuilder[paramTypes.Length];
for (var i = 0; i < paramTypes.Length; i++)
{
locals[i] = il.DeclareLocal(paramTypes[i], true);
}
for (var i = 0; i < paramTypes.Length; i++)
{
il.Emit(OpCodes.Ldarg_1);
EmitFastInt(il, i);
il.Emit(OpCodes.Ldelem_Ref);
EmitCastToReference(il, paramTypes[i]);
il.Emit(OpCodes.Stloc, locals[i]);
}
if (!methodInfo.IsStatic)
{
il.Emit(OpCodes.Ldarg_0);
}
for (var i = 0; i < paramTypes.Length; i++)
{
if (ps[i].ParameterType.IsByRef)
{
il.Emit(OpCodes.Ldloca_S, locals[i]);
}
else
{
il.Emit(OpCodes.Ldloc, locals[i]);
}
}
if (methodInfo.IsStatic)
{
il.EmitCall(OpCodes.Call, methodInfo, null);
}
else
{
il.EmitCall(OpCodes.Callvirt, methodInfo, null);
}
if (methodInfo.ReturnType == typeof(void))
{
il.Emit(OpCodes.Ldnull);
}
else
{
EmitBoxIfNeeded(il, methodInfo.ReturnType);
}
for (var i = 0; i < paramTypes.Length; i++)
{
if (ps[i].ParameterType.IsByRef)
{
il.Emit(OpCodes.Ldarg_1);
EmitFastInt(il, i);
il.Emit(OpCodes.Ldloc, locals[i]);
if (locals[i].LocalType.IsValueType)
{
il.Emit(OpCodes.Box, locals[i].LocalType);
}
il.Emit(OpCodes.Stelem_Ref);
}
}
il.Emit(OpCodes.Ret);
var invoker= (Func<object, object[], object>)dynamicMethod.CreateDelegate(typeof(Func<object, object[], object>));
return invoker;
}
/// <summary>
/// 构建表达式树调用
/// </summary>
/// <param name="method"></param>
/// <returns></returns>
protected Func<object, object[], object> CreateExpressionInvoker(MethodInfo method)
{
var instance = Expression.Parameter(typeof(object), "instance");
var parameters = Expression.Parameter(typeof(object[]), "parameters");
var instanceCast = method.IsStatic ? null : Expression.Convert(instance, method.DeclaringType);
var parametersCast = method.GetParameters().Select((p, i) =>
{
var parameter = Expression.ArrayIndex(parameters, Expression.Constant(i));
return Expression.Convert(parameter, p.ParameterType);
});
var body = Expression.Call(instanceCast, method, parametersCast);
if (method.ReturnType == typeof(Task))
{
this.HasReturn = false;
this.TaskType = TaskReturnType.Task;
var bodyCast = Expression.Convert(body, typeof(object));
return Expression.Lambda<Func<object, object[], object>>(bodyCast, instance, parameters).Compile();
}
else if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
{
this.TaskType = TaskReturnType.TaskObject;
this.HasReturn = true;
this.ReturnType = method.ReturnType.GetGenericArguments()[0];
var bodyCast = Expression.Convert(body, typeof(object));
return Expression.Lambda<Func<object, object[], object>>(bodyCast, instance, parameters).Compile();
}
else if (method.ReturnType == typeof(void))
{
this.HasReturn = false;
this.TaskType = TaskReturnType.None;
var action = Expression.Lambda<Action<object, object[]>>(body, instance, parameters).Compile();
return (_instance, _parameters) =>
{
action.Invoke(_instance, _parameters);
ThrowHelper.ThrowInvalidEnumArgumentException(this.ReturnKind);
return null;
};
}
else
{
this.HasReturn = true;
this.TaskType = TaskReturnType.None;
this.ReturnType = method.ReturnType;
var bodyCast = Expression.Convert(body, typeof(object));
return Expression.Lambda<Func<object, object[], object>>(bodyCast, instance, parameters).Compile();
}
}
private static void EmitBoxIfNeeded(ILGenerator il, System.Type type)
#endregion Invoke
/// <summary>
/// 通过源生成器创建动态方法信息。
/// </summary>
/// <returns>动态方法信息接口。</returns>
private IDynamicMethodInfo CreateDynamicMethodInfoFromSG()
{
if (type.IsValueType)
var typeName = $"{GeneratorTypeNamespace}.__{StringExtension.MakeIdentifier(this.Info.DeclaringType.FullName)}MethodExtension";
var type = this.Info.DeclaringType.Assembly.GetType(typeName);
if (type == null)
{
il.Emit(OpCodes.Box, type);
}
}
private static void EmitCastToReference(ILGenerator il, System.Type type)
{
if (type.IsValueType)
{
il.Emit(OpCodes.Unbox_Any, type);
}
else
{
il.Emit(OpCodes.Castclass, type);
}
}
private static void EmitFastInt(ILGenerator il, int value)
{
switch (value)
{
case -1:
il.Emit(OpCodes.Ldc_I4_M1);
return;
case 0:
il.Emit(OpCodes.Ldc_I4_0);
return;
case 1:
il.Emit(OpCodes.Ldc_I4_1);
return;
case 2:
il.Emit(OpCodes.Ldc_I4_2);
return;
case 3:
il.Emit(OpCodes.Ldc_I4_3);
return;
case 4:
il.Emit(OpCodes.Ldc_I4_4);
return;
case 5:
il.Emit(OpCodes.Ldc_I4_5);
return;
case 6:
il.Emit(OpCodes.Ldc_I4_6);
return;
case 7:
il.Emit(OpCodes.Ldc_I4_7);
return;
case 8:
il.Emit(OpCodes.Ldc_I4_8);
return;
return default;
}
if (value > -129 && value < 128)
var methodName = $"{this.Info.GetDeterminantName()}ClassProperty";
var property = type.GetProperty(methodName, BindingFlags.Public | BindingFlags.Static);
if (property == null)
{
il.Emit(OpCodes.Ldc_I4_S, (SByte)value);
}
else
{
il.Emit(OpCodes.Ldc_I4, value);
return default;
}
return (IDynamicMethodInfo)property.GetValue(null);
}
}

View File

@@ -12,23 +12,29 @@
namespace TouchSocket.Core;
/// <summary>
/// Task类型
/// 表示方法的返回类型
/// </summary>
public enum TaskReturnType
public enum MethodReturnKind
{
/// <summary>
/// 没有Task
/// 方法没有返回值。
/// </summary>
None,
Void,
/// <summary>
/// 仅返回Task
/// 方法返回一个对象。
/// </summary>
Task,
Object,
/// <summary>
/// 返回Task的值
/// 方法返回一个可等待的任务。
/// </summary>
TaskObject
Awaitable,
/// <summary>
/// 方法返回一个可等待的任务,并且任务有返回值。
/// </summary>
AwaitableObject
}

View File

@@ -0,0 +1,34 @@
// ------------------------------------------------------------------------------
// 此代码版权除特别声明或在XREF结尾的命名空间的代码归作者本人若汝棋茗所有
// 源代码使用协议遵循本仓库的开源协议及附加协议若本仓库没有设置则按MIT开源协议授权
// CSDN博客https://blog.csdn.net/qq_40374647
// 哔哩哔哩视频https://space.bilibili.com/94253567
// Gitee源代码仓库https://gitee.com/RRQM_Home
// Github源代码仓库https://github.com/RRQM
// API首页https://touchsocket.net/
// 交流QQ群234762506
// 感谢您的下载和使用
// ------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace TouchSocket.Core;
internal class ReflectDynamicMethodInfo : DynamicMethodInfoBase
{
private readonly MethodInfo m_method;
public ReflectDynamicMethodInfo(MethodInfo method) : base(method)
{
this.m_method = method;
}
public override object Invoke(object instance, object[] parameters)
{
return m_method.Invoke(instance, parameters);
}
}

View File

@@ -14,6 +14,10 @@
<None Include="..\TouchSocket.Core.SourceGenerator\bin\$(Configuration)\netstandard2.0\TouchSocket.Core.SourceGenerator.dll" PackagePath="analyzers\dotnet\cs" Pack="true" Visible="false" />
</ItemGroup>
<!--<ItemGroup Condition="'$(TargetFramework)'=='net45'">
<Analyzer Include="..\TouchSocket.Core.SourceGenerator\bin\$(Configuration)\netstandard2.0\TouchSocket.Core.SourceGenerator.dll" />
</ItemGroup>-->
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
<PackageReference Include="System.Reflection.Emit.ILGeneration" Version="4.7.0" PrivateAssets="All" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" PrivateAssets="All" />

View File

@@ -44,14 +44,14 @@ public interface IWaitDataBase<T> : IDisposableObject
/// <summary>
/// 使等待的线程继续执行。
/// </summary>
/// <returns>如果操作成功则返回true否则返回false。</returns>
/// <returns>如果操作成功,则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
bool Set();
/// <summary>
/// 使等待的线程继续执行,并设置等待结果。
/// </summary>
/// <param name="waitResult">等待结果。</param>
/// <returns>如果操作成功则返回true否则返回false。</returns>
/// <returns>如果操作成功,则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
bool Set(T waitResult);
/// <summary>

View File

@@ -1067,7 +1067,7 @@ public abstract class DmtpActor : DisposableObject, IDmtpActor
channel = null;
return false;
}
channelOut.SetUsing();
channelOut.MakeUsing();
channel = channelOut;
return true;
}
@@ -1133,7 +1133,7 @@ public abstract class DmtpActor : DisposableObject, IDmtpActor
{
var channel = new InternalChannel(this, targetId, result.Metadata);
channel.SetId(result.ChannelId);
channel.SetUsing();
channel.MakeUsing();
if (this.m_userChannels.TryAdd(result.ChannelId, channel))
{
return channel;

View File

@@ -68,7 +68,7 @@ public interface IDmtpActor : IDisposableObject, IOnlineClient, IClosableClient,
/// 判断指定Id的通道是否已经存在
/// </summary>
/// <param name="id">要判断的通道Id</param>
/// <returns>如果通道存在返回true否则返回false</returns>
/// <returns>如果通道存在返回<see langword="true"/>,否则返回<see langword="false"/></returns>
bool ChannelExisted(int id);
/// <summary>
@@ -108,7 +108,7 @@ public interface IDmtpActor : IDisposableObject, IOnlineClient, IClosableClient,
/// </summary>
/// <param name="id">要订阅的通道的标识符。</param>
/// <param name="channel">订阅的通道对象,成功时返回此参数。</param>
/// <returns>如果订阅成功则返回true如果通道不存在或发生错误则返回false。</returns>
/// <returns>如果订阅成功则返回<see langword="true"/>;如果通道不存在或发生错误则返回<see langword="false"/>。</returns>
bool TrySubscribeChannel(int id, out IDmtpChannel channel);
#endregion IDmtpChannel

View File

@@ -87,14 +87,14 @@ public partial interface IDmtpChannel : IDisposable, IEnumerable<ByteBlock>
/// </summary>
/// <param name="operationMes">可选参数,用于提供取消操作的详细信息</param>
/// <returns>返回一个Task对象表示异步取消操作的完成</returns>
Task CancelAsync(string operationMes = null);
Task<Result> CancelAsync(string operationMes = null);
/// <summary>
/// 异步完成操作
/// </summary>
/// <param name="operationMes">操作信息可选参数默认为null</param>
/// <returns>返回一个Task对象表示异步操作的完成</returns>
Task CompleteAsync(string operationMes = null);
Task<Result> CompleteAsync(string operationMes = null);
/// <summary>
/// 获取当前的有效数据。在使用之后,请进行显式的<see cref="IDisposable.Dispose"/>调用。
@@ -107,7 +107,7 @@ public partial interface IDmtpChannel : IDisposable, IEnumerable<ByteBlock>
/// </summary>
/// <param name="operationMes"></param>
/// <returns></returns>
Task HoldOnAsync(string operationMes = null);
Task<Result> HoldOnAsync(string operationMes = null);
/// <summary>
/// 转向下个元素

View File

@@ -22,7 +22,7 @@ using TouchSocket.Core;
namespace TouchSocket.Dmtp;
[DebuggerDisplay("Id={Id},Status={Status}")]
internal sealed partial class InternalChannel : DisposableObject, IDmtpChannel
internal sealed partial class InternalChannel : SafetyDisposableObject, IDmtpChannel
{
private readonly DmtpActor m_actor;
private readonly ConcurrentQueue<ChannelPackage> m_dataQueue;
@@ -94,11 +94,11 @@ internal sealed partial class InternalChannel : DisposableObject, IDmtpChannel
#region
public async Task CancelAsync(string operationMes = null)
public async Task<Result> CancelAsync(string operationMes = null)
{
if ((byte)this.Status > 3)
{
return;
return Result.Success;
}
try
{
@@ -114,53 +114,73 @@ internal sealed partial class InternalChannel : DisposableObject, IDmtpChannel
};
await this.m_actor.SendChannelPackageAsync(channelPackage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
this.m_lastOperationTime = DateTimeOffset.UtcNow;
return Result.Success;
}
catch
catch (Exception ex)
{
return Result.FromException(ex);
}
}
public async Task CompleteAsync(string operationMes = null)
public async Task<Result> CompleteAsync(string operationMes = null)
{
if ((byte)this.Status > 3)
{
return;
return Result.Success;
}
try
{
this.RequestComplete(true);
var channelPackage = new ChannelPackage()
{
ChannelId = this.Id,
RunNow = true,
DataType = ChannelDataType.CompleteOrder,
Message = operationMes,
SourceId = this.m_actor.Id,
TargetId = this.TargetId
};
await this.m_actor.SendChannelPackageAsync(channelPackage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
this.m_lastOperationTime = DateTimeOffset.UtcNow;
return Result.Success;
}
catch (Exception ex)
{
return Result.FromException(ex);
}
this.RequestComplete(true);
var channelPackage = new ChannelPackage()
{
ChannelId = this.Id,
RunNow = true,
DataType = ChannelDataType.CompleteOrder,
Message = operationMes,
SourceId = this.m_actor.Id,
TargetId = this.TargetId
};
await this.m_actor.SendChannelPackageAsync(channelPackage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
this.m_lastOperationTime = DateTimeOffset.UtcNow;
}
public async Task HoldOnAsync(string operationMes = null)
public async Task<Result> HoldOnAsync(string operationMes = null)
{
if ((byte)this.Status > 3)
{
return;
return Result.Success;
}
var channelPackage = new ChannelPackage()
try
{
ChannelId = this.Id,
RunNow = true,
DataType = ChannelDataType.HoldOnOrder,
Message = operationMes,
SourceId = this.m_actor.Id,
TargetId = this.TargetId
};
await this.m_actor.SendChannelPackageAsync(channelPackage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
this.m_lastOperationTime = DateTimeOffset.UtcNow;
var channelPackage = new ChannelPackage()
{
ChannelId = this.Id,
RunNow = true,
DataType = ChannelDataType.HoldOnOrder,
Message = operationMes,
SourceId = this.m_actor.Id,
TargetId = this.TargetId
};
await this.m_actor.SendChannelPackageAsync(channelPackage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
this.m_lastOperationTime = DateTimeOffset.UtcNow;
return Result.Success;
}
catch (Exception ex)
{
return Result.FromException(ex);
}
}
protected override void Dispose(bool disposing)
protected override void SafetyDispose(bool disposing)
{
//不判断disposing能够让GC也能发送释放指令
try
@@ -186,9 +206,8 @@ internal sealed partial class InternalChannel : DisposableObject, IDmtpChannel
catch
{
}
base.Dispose(disposing);
}
#endregion
public ByteBlock GetCurrent()
@@ -394,7 +413,7 @@ internal sealed partial class InternalChannel : DisposableObject, IDmtpChannel
this.Id = id;
}
internal void SetUsing()
internal void MakeUsing()
{
this.Using = true;
}

View File

@@ -44,7 +44,7 @@ public readonly struct RouteType
/// </summary>
/// <param name="a">第一个RouteType对象</param>
/// <param name="b">第二个RouteType对象</param>
/// <returns>如果两个对象相等返回true否则返回false</returns>
/// <returns>如果两个对象相等返回<see langword="true"/>,否则返回<see langword="false"/></returns>
public static bool operator ==(RouteType a, RouteType b)
{
return a.m_value == b.m_value;
@@ -55,7 +55,7 @@ public readonly struct RouteType
/// </summary>
/// <param name="a">第一个RouteType对象</param>
/// <param name="b">第二个RouteType对象</param>
/// <returns>如果两个对象不相等返回true否则返回false</returns>
/// <returns>如果两个对象不相等返回<see langword="true"/>,否则返回<see langword="false"/></returns>
public static bool operator !=(RouteType a, RouteType b)
{
return a.m_value != b.m_value;
@@ -65,7 +65,7 @@ public readonly struct RouteType
/// 重写Equals方法用于比较两个RouteType对象是否相等
/// </summary>
/// <param name="obj">要比较的对象</param>
/// <returns>如果对象相等返回true否则返回false</returns>
/// <returns>如果对象相等返回<see langword="true"/>,否则返回<see langword="false"/></returns>
public override bool Equals(object obj)
{
return obj is RouteType type && this == type;

View File

@@ -58,24 +58,28 @@ public static class DmtpActorExtension
}
/// <inheritdoc cref="IDmtpActor.CreateChannelAsync(Metadata)"/>
[AsyncToSyncWarning]
public static IDmtpChannel CreateChannel(this IDmtpActorObject client, Metadata metadata = default)
{
return client.DmtpActor.CreateChannelAsync(metadata).GetFalseAwaitResult();
}
/// <inheritdoc cref="IDmtpActor.CreateChannelAsync(int, Metadata)"/>
[AsyncToSyncWarning]
public static IDmtpChannel CreateChannel(this IDmtpActorObject client, int id, Metadata metadata = default)
{
return client.DmtpActor.CreateChannelAsync(id, metadata).GetFalseAwaitResult();
}
/// <inheritdoc cref="IDmtpActor.CreateChannelAsync(string, int, Metadata)"/>
[AsyncToSyncWarning]
public static IDmtpChannel CreateChannel(this IDmtpActorObject client, string targetId, int id, Metadata metadata = default)
{
return client.DmtpActor.CreateChannelAsync(targetId, id, metadata).GetFalseAwaitResult();
}
/// <inheritdoc cref="IDmtpActor.CreateChannelAsync(string, Metadata)"/>
[AsyncToSyncWarning]
public static IDmtpChannel CreateChannel(this IDmtpActorObject client, string targetId, Metadata metadata = default)
{
return client.DmtpActor.CreateChannelAsync(targetId, metadata).GetFalseAwaitResult();
@@ -144,7 +148,7 @@ public static class DmtpActorExtension
/// <returns>返回一个布尔值,表示是否成功发送数据。</returns>
public static async Task<bool> TrySendAsync(this IDmtpActorObject client, ushort protocol)
{
// 尝试发送数据如果成功则返回true失败则返回false。
// 尝试发送数据,如果成功则返回<see langword="true"/>,失败则返回<see langword="false"/>
try
{
// 使用空的内存数据发送协议命令。
@@ -153,7 +157,7 @@ public static class DmtpActorExtension
}
catch
{
// 如果发送过程中发生异常则返回false。
// 如果发送过程中发生异常,则返回<see langword="false"/>
return false;
}
}
@@ -219,12 +223,12 @@ public static class DmtpActorExtension
{
// 实际执行发送操作。
await SendAsync(client, protocol, package, maxSize).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
// 发送成功返回true。
// 发送成功,返回<see langword="true"/>
return true;
}
catch
{
// 发送失败返回false。
// 发送失败,返回<see langword="false"/>
return false;
}
}
@@ -235,10 +239,10 @@ public static class DmtpActorExtension
/// <param name="client">要发送包的客户端。</param>
/// <param name="protocol">使用的协议。</param>
/// <param name="package">要发送的包。</param>
/// <returns>如果发送成功则返回true否则返回false。</returns>
/// <returns>如果发送成功,则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
public static async Task<bool> TrySendAsync(this IDmtpActorObject client, ushort protocol, IPackage package)
{
// 尝试发送包如果成功则返回true失败则返回false
// 尝试发送包,如果成功则返回<see langword="true"/>,失败则返回<see langword="false"/>
try
{
// 实际执行发送操作64KB是根据业务需求预设的限制

View File

@@ -68,13 +68,13 @@ public class FileSection : PackageBase
/// 判断基本信息是否一致。
/// </summary>
/// <param name="fileSection">待比较的文件段对象。</param>
/// <returns>如果待比较的文件段对象的基本信息与当前对象一致则返回true否则返回false。</returns>
/// <returns>如果待比较的文件段对象的基本信息与当前对象一致,则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
public bool Equals(FileSection fileSection)
{
// 检查待比较的文件段对象是否为null
if (fileSection == null)
{
// 如果是null直接返回false因为无法比较
// 如果是null直接返回<see langword="false"/>,因为无法比较
return false;
}
else

View File

@@ -21,7 +21,7 @@ public static class TransferTypeExtension
/// 表示当前传输类型是否属于<see cref="TransferType.Pull"/>、<see cref="TransferType.SmallPull"/>其中的一种。
/// </summary>
/// <param name="transferType">要检查的传输类型。</param>
/// <returns>如果传输类型是<see cref="TransferType.Pull"/>或<see cref="TransferType.SmallPull"/>则返回true否则返回false。</returns>
/// <returns>如果传输类型是<see cref="TransferType.Pull"/>或<see cref="TransferType.SmallPull"/>,则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
public static bool IsPull(this TransferType transferType)
{
return transferType == TransferType.Pull || transferType == TransferType.SmallPull;

View File

@@ -49,7 +49,7 @@ public interface IFileResourceController : IDisposable
/// </summary>
/// <param name="resourceHandle">资源句柄,标识需要释放的文件资源定位器</param>
/// <param name="locator">输出参数,返回被释放的文件资源定位器</param>
/// <returns>如果成功释放文件资源定位器则返回true否则返回false</returns>
/// <returns>如果成功释放文件资源定位器,则返回<see langword="true"/>;否则返回<see langword="false"/></returns>
bool TryReleaseFileResourceLocator(int resourceHandle, out FileResourceLocator locator);
/// <summary>
@@ -57,7 +57,7 @@ public interface IFileResourceController : IDisposable
/// </summary>
/// <param name="resourceHandle">文件句柄,用于标识特定的文件资源。</param>
/// <param name="fileResourceLocator">输出参数,返回文件的资源定位器。</param>
/// <returns>如果成功获取资源定位器返回true否则返回false。</returns>
/// <returns>如果成功获取资源定位器,返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
bool TryGetFileResourceLocator(int resourceHandle, out FileResourceLocator fileResourceLocator);
/// <summary>

View File

@@ -79,7 +79,7 @@ public class HttpRequest : HttpBase
/// <summary>
/// 保持连接。
/// <para>
/// 一般的当是http1.1时如果没有显式的Connection: close即返回true。当是http1.0时如果没有显式的Connection: Keep-Alive即返回false。
/// 一般的当是http1.1时如果没有显式的Connection: close即返回<see langword="true"/>。当是http1.0时如果没有显式的Connection: Keep-Alive即返回<see langword="false"/>
/// </para>
/// </summary>
public bool KeepAlive

View File

@@ -384,7 +384,7 @@ public static partial class HttpExtensions
/// </summary>
/// <param name="request">请求对象用于获取待对比的相对URL。</param>
/// <param name="url">待对比的目标URL字符串。</param>
/// <returns>如果两个URL都不为null且在忽略大小写的情况下相等则返回true否则返回false。</returns>
/// <returns>如果两个URL都不为null且在忽略大小写的情况下相等则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
public static bool UrlEquals<TRequest>(this TRequest request, string url) where TRequest : HttpRequest
{
// 检查两个URL是否都不为null并且在文化无关的大小写不敏感的情况下是否相等
@@ -510,7 +510,7 @@ public static partial class HttpExtensions
/// 判断当前请求是否为Delete操作
/// </summary>
/// <param name="request">请求对象,用于检查请求方法</param>
/// <returns>如果请求方法为Delete则返回true否则返回false</returns>
/// <returns>如果请求方法为Delete则返回<see langword="true"/>;否则返回<see langword="false"/></returns>
public static bool IsDelete<TRequest>(this TRequest request) where TRequest : HttpRequest
{
return request.Method == HttpMethod.Delete;
@@ -520,7 +520,7 @@ public static partial class HttpExtensions
/// 判断当前请求是否为Get请求
/// </summary>
/// <param name="request">请求对象,用于检查其请求方法</param>
/// <returns>如果请求方法是Get则返回true否则返回false</returns>
/// <returns>如果请求方法是Get则返回<see langword="true"/>;否则返回<see langword="false"/></returns>
public static bool IsGet<TRequest>(this TRequest request) where TRequest : HttpRequest
{
return request.Method == HttpMethod.Get;
@@ -531,7 +531,7 @@ public static partial class HttpExtensions
/// </summary>
/// <param name="request">待检查的HTTP请求</param>
/// <param name="method">要判断的HTTP方法类型如"Get"、"Post"</param>
/// <returns>如果请求的方法类型与指定的方法一致则返回true否则返回false</returns>
/// <returns>如果请求的方法类型与指定的方法一致,则返回<see langword="true"/>;否则返回<see langword="false"/></returns>
public static bool IsMethod<TRequest>(this TRequest request, string method) where TRequest : HttpRequest
{
return request.Method == new HttpMethod(method);
@@ -541,7 +541,7 @@ public static partial class HttpExtensions
/// 判断当前请求是否为Post请求
/// </summary>
/// <param name="request">请求对象泛型参数必须是HttpRequest的子类或实现</param>
/// <returns>如果当前请求方法是Post则返回true否则返回false</returns>
/// <returns>如果当前请求方法是Post则返回<see langword="true"/>;否则返回<see langword="false"/></returns>
public static bool IsPost<TRequest>(this TRequest request) where TRequest : HttpRequest
{
// 直接比较请求对象的Method属性是否为HttpMethod.Post以判断是否为Post请求
@@ -552,7 +552,7 @@ public static partial class HttpExtensions
/// 判断请求是否为PUT方法
/// </summary>
/// <param name="request">请求对象类型为HttpRequest的泛型实例</param>
/// <returns>如果请求方法为PUT则返回true否则返回false</returns>
/// <returns>如果请求方法为PUT则返回<see langword="true"/>;否则返回<see langword="false"/></returns>
public static bool IsPut<TRequest>(this TRequest request) where TRequest : HttpRequest
{
return request.Method == HttpMethod.Put;
@@ -566,7 +566,7 @@ public static partial class HttpExtensions
/// 判断请求是否接受Gzip压缩。
/// </summary>
/// <param name="request">请求对象,用于获取请求的接受编码。</param>
/// <returns>如果请求接受Gzip压缩则返回true否则返回false。</returns>
/// <returns>如果请求接受Gzip压缩则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
public static bool IsAcceptGzip<TRequest>(this TRequest request) where TRequest : HttpRequest
{
// 获取请求头中的接受编码信息
@@ -580,7 +580,7 @@ public static partial class HttpExtensions
/// 判断请求头中是否包含升级连接
/// </summary>
/// <param name="request">请求对象泛型参数要求是HttpRequest的子类或实现</param>
/// <returns>如果请求头中包含升级连接则返回true否则返回false</returns>
/// <returns>如果请求头中包含升级连接,则返回<see langword="true"/>;否则返回<see langword="false"/></returns>
public static bool IsUpgrade<TRequest>(this TRequest request) where TRequest : HttpRequest
{
// 比较请求头中的连接类型是否为升级类型,忽略大小写

View File

@@ -57,7 +57,7 @@ public class ReadonlyMemoryHttpContent : HttpContent
return true;
}
//返回false提示后续数据可能太大通过WriteContent执行。
//返回<see langword="false"/>提示后续数据可能太大通过WriteContent执行。
return false;
}

View File

@@ -22,7 +22,7 @@ public interface IContentTypeProvider
/// </summary>
/// <param name="subpath">文件路径</param>
/// <param name="contentType">MIME类型</param>
/// <returns>如果找到匹配的MIME类型则返回true否则返回false</returns>
/// <returns>如果找到匹配的MIME类型则返回<see langword="true"/>,否则返回<see langword="false"/></returns>
bool TryGetContentType(string subpath, out string contentType);
/// <summary>

View File

@@ -52,7 +52,7 @@ public interface IFormCollection : IEnumerable<KeyValuePair<string, string>>
/// 判断集合中是否包含指定的键
/// </summary>
/// <param name="key">要检查的键</param>
/// <returns>如果集合包含指定的键则返回true否则返回false</returns>
/// <returns>如果集合包含指定的键,则返回<see langword="true"/>;否则返回<see langword="false"/></returns>
bool ContainsKey(string key);
/// <summary>
@@ -60,6 +60,6 @@ public interface IFormCollection : IEnumerable<KeyValuePair<string, string>>
/// </summary>
/// <param name="key">要获取值的键</param>
/// <param name="value">与指定键关联的值如果键不存在则为null</param>
/// <returns>如果键存在于集合中则返回true否则返回false</returns>
/// <returns>如果键存在于集合中,则返回<see langword="true"/>;否则返回<see langword="false"/></returns>
bool TryGetValue(string key, out string value);
}

View File

@@ -98,7 +98,7 @@ public class StaticFilesPool : DisposableObject
/// <param name="key">缓存键</param>
/// <param name="value">缓存值,以字节数组形式存储</param>
/// <param name="millisecondsTimeout">缓存条目的超时时间,以毫秒为单位</param>
/// <returns>始终返回true表示添加操作已完成</returns>
/// <returns>始终返回<see langword="true"/>,表示添加操作已完成</returns>
public bool AddEntry(string key, byte[] value, TimeSpan millisecondsTimeout)
{
// 使用WriteLock确保在添加缓存条目时数据的一致性
@@ -118,7 +118,7 @@ public class StaticFilesPool : DisposableObject
/// <param name="key">要添加的条目的键。</param>
/// <param name="value">要添加的条目的值,包含文件信息。</param>
/// <param name="millisecondsTimeout">条目过期的时间段,以毫秒为单位。</param>
/// <returns>总是返回true表示条目已成功添加。</returns>
/// <returns>总是返回<see langword="true"/>,表示条目已成功添加。</returns>
public bool AddEntry(string key, FileInfo value, TimeSpan millisecondsTimeout)
{
// 使用WriteLock确保在添加条目时数据的一致性
@@ -175,7 +175,7 @@ public class StaticFilesPool : DisposableObject
/// </summary>
/// <param name="key">要查找的键。</param>
/// <param name="cacheEntry">找到的缓存项,通过引用返回。</param>
/// <returns>如果找到缓存项则返回true否则返回false。</returns>
/// <returns>如果找到缓存项则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
public bool TryFindEntry(string key, out StaticEntry cacheEntry)
{
// 使用读锁来确保并发访问时的一致性
@@ -238,7 +238,7 @@ public class StaticFilesPool : DisposableObject
/// 检查指定路径的文件夹是否存在于集合中。
/// </summary>
/// <param name="path">要检查的文件夹路径。</param>
/// <returns>如果文件夹存在于集合中则返回true否则返回false。</returns>
/// <returns>如果文件夹存在于集合中,则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
public bool ContainsFolder(string path)
{
// 使用读取锁确保线程安全

View File

@@ -134,7 +134,7 @@ public class WSDataFrame : DisposableObject, IRequestInfo, IRequestInfoBuilder,
header = (header << 1) + (this.RSV1 ? 1 : 0);
header = (header << 1) + (this.RSV2 ? 1 : 0);
header = (header << 1) + (this.RSV3 ? 1 : 0);
header = (header << 4) + (ushort)this.Opcode;
header = (header << 4) + (byte)this.Opcode;
header = this.Mask ? (header << 1) + 1 : (header << 1) + 0;

View File

@@ -69,7 +69,7 @@ public sealed class WebSocketMessageCombinator
/// </summary>
/// <param name="dataFrame">待组合的数据帧。</param>
/// <param name="webSocketMessage">组合成功的WebSocket消息。</param>
/// <returns>如果成功组合则返回true否则返回false。</returns>
/// <returns>如果成功组合则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
public bool TryCombine(WSDataFrame dataFrame, out WebSocketMessage webSocketMessage)
{
var data = dataFrame.PayloadData;

View File

@@ -13,9 +13,10 @@
namespace TouchSocket.Http.WebSockets;
/// <summary>
/// WebSocket数据类型
/// WebSocket数据类型
/// 支持最大值为2^4不能超过16即0-15
/// </summary>
public enum WSDataType : ushort
public enum WSDataType : byte
{
/// <summary>
/// 表示一个中间数据包

View File

@@ -105,24 +105,7 @@ public abstract class WebSocketCommandLinePlugin : PluginBase, IWebSocketReceive
try
{
object result;
switch (method.TaskType)
{
case TaskReturnType.Task:
await method.InvokeAsync(this, os).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
result = default;
break;
case TaskReturnType.TaskObject:
result = await method.InvokeObjectAsync(this, os).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
break;
case TaskReturnType.None:
default:
result = method.Invoke(this, os);
break;
}
var result=await method.InvokeAsync(this, os).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
if (method.HasReturn)
{
await webSocket.SendAsync(this.Converter.Serialize(null, result)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);

View File

@@ -102,7 +102,7 @@ public class ModbusRtuMaster : SerialPortClientBase, IModbusRtuMaster
/// </summary>
/// <param name="modbusRequest">Modbus请求</param>
/// <param name="response">Modbus响应</param>
/// <returns>如果请求和响应匹配则返回true否则返回false</returns>
/// <returns>如果请求和响应匹配则返回<see langword="true"/>,否则返回<see langword="false"/></returns>
protected virtual bool SetRun(IModbusRequest modbusRequest, IModbusResponse response)
{
if (modbusRequest.SlaveId != response.SlaveId)

View File

@@ -38,7 +38,7 @@ public readonly ref struct MqttV5PropertiesReader<TByteBlock> where TByteBlock :
/// </summary>
/// <param name="byteBlock">字节块引用。</param>
/// <param name="identifier">读取的属性标识符。</param>
/// <returns>如果读取成功则返回true否则返回false。</returns>
/// <returns>如果读取成功,则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
public bool TryRead(ref TByteBlock byteBlock, out MqttPropertyId identifier)
{
if (byteBlock.Position >= this.m_endPosition)

View File

@@ -349,7 +349,7 @@ public abstract class NamedPipeSessionClientBase : ResolverConfigObject, INamedP
/// </summary>
/// <param name="id">客户端的唯一标识符</param>
/// <param name="sessionClient">输出参数,用于返回找到的客户端实例</param>
/// <returns>如果找到对应的客户端则返回true否则返回false</returns>
/// <returns>如果找到对应的客户端,则返回<see langword="true"/>;否则返回<see langword="false"/></returns>
protected bool ProtectedTryGetClient(string id, out NamedPipeSessionClientBase sessionClient)
{
// 调用内部方法尝试获取客户端

View File

@@ -526,12 +526,12 @@ public abstract class RpcAttribute : Attribute
if (isAsync)
{
// 如果返回类型为空则默认为Task否则构造Task<T>类型
return rpcMethod.ReturnType == null ? "Task" : $"Task<{this.GetProxyParameterName(rpcMethod.Info.ReturnParameter)}>";
return rpcMethod.RealReturnType == null ? "Task" : $"Task<{this.GetProxyParameterName(rpcMethod.Info.ReturnParameter)}>";
}
else
{
// 当非异步调用时返回void或方法的返回参数名
return rpcMethod.ReturnType == null ? "void" : this.GetProxyParameterName(rpcMethod.Info.ReturnParameter);
return rpcMethod.RealReturnType == null ? "void" : this.GetProxyParameterName(rpcMethod.Info.ReturnParameter);
}
}

View File

@@ -291,9 +291,9 @@ public static class CodeGenerator
{
if (attributeType == att.GetType())
{
if (rpcMethod.ReturnType != null)
if (rpcMethod.RealReturnType != null)
{
classCodeGenerator.AddTypeString(rpcMethod.ReturnType);
classCodeGenerator.AddTypeString(rpcMethod.RealReturnType);
}
var psTypes = rpcMethod.GetNormalParameters().Select(a => a.Type);

View File

@@ -75,23 +75,22 @@ public abstract class RpcDispatchProxy<TClient, TAttribute> : DispatchProxy wher
object result = default;
switch (rpcMethod.TaskType)
switch (rpcMethod.ReturnKind)
{
case TaskReturnType.Task:
case MethodReturnKind.Awaitable:
{
result = this.GetClient().InvokeAsync(invokeKey, rpcMethod.ReturnType, invokeOption, ps);
result = this.GetClient().InvokeAsync(invokeKey, rpcMethod.RealReturnType, invokeOption, ps);
break;
}
case TaskReturnType.TaskObject:
case MethodReturnKind.AwaitableObject:
{
result = this.GetClient().InvokeAsync(invokeKey, rpcMethod.ReturnType, invokeOption, ps).GetFalseAwaitResult();
result = this.GetClient().InvokeAsync(invokeKey, rpcMethod.RealReturnType, invokeOption, ps).GetFalseAwaitResult();
result = value.GenericMethod.Invoke(default, result);
break;
}
case TaskReturnType.None:
default:
{
result = this.GetClient().Invoke(invokeKey, rpcMethod.ReturnType, invokeOption, ps);
result = this.GetClient().Invoke(invokeKey, rpcMethod.RealReturnType, invokeOption, ps);
break;
}
}
@@ -117,7 +116,7 @@ public abstract class RpcDispatchProxy<TClient, TAttribute> : DispatchProxy wher
InvokeKey = invokeKey,
RpcMethod = rpcMethod,
InvokeOption = invokeOption,
GenericMethod = rpcMethod.TaskType == TaskReturnType.TaskObject ? new Method(this.m_fromResultMethod.MakeGenericMethod(rpcMethod.ReturnType)) : default
GenericMethod = rpcMethod.ReturnKind == MethodReturnKind.AwaitableObject ? new Method(this.m_fromResultMethod.MakeGenericMethod(rpcMethod.RealReturnType)) : default
};
}

View File

@@ -91,23 +91,22 @@ public abstract class RpcRealityProxy<T, TClient, TAttribute> : RpcRealityProxyB
object result;
switch (rpcMethod.TaskType)
switch (rpcMethod.ReturnKind)
{
case TaskReturnType.Task:
case MethodReturnKind.Awaitable:
{
result = this.GetClient().InvokeAsync(invokeKey, rpcMethod.ReturnType, invokeOption, ps);
result = this.GetClient().InvokeAsync(invokeKey, rpcMethod.RealReturnType, invokeOption, ps);
break;
}
case TaskReturnType.TaskObject:
case MethodReturnKind.AwaitableObject:
{
result = this.GetClient().InvokeAsync(invokeKey, rpcMethod.ReturnType, invokeOption, ps).GetFalseAwaitResult();
result = this.GetClient().InvokeAsync(invokeKey, rpcMethod.RealReturnType, invokeOption, ps).GetFalseAwaitResult();
result = value.GenericMethod.Invoke(default, result);
break;
}
case TaskReturnType.None:
default:
{
result = this.GetClient().InvokeAsync(invokeKey, rpcMethod.ReturnType, invokeOption, ps).GetFalseAwaitResult();
result = this.GetClient().InvokeAsync(invokeKey, rpcMethod.RealReturnType, invokeOption, ps).GetFalseAwaitResult();
break;
}
}
@@ -132,7 +131,7 @@ public abstract class RpcRealityProxy<T, TClient, TAttribute> : RpcRealityProxyB
InvokeKey = invokeKey,
RpcMethod = rpcMethod,
InvokeOption = invokeOption,
GenericMethod = rpcMethod.TaskType == TaskReturnType.TaskObject ? new Method(this.m_fromResultMethod.MakeGenericMethod(rpcMethod.ReturnType)) : default
GenericMethod = rpcMethod.ReturnKind == MethodReturnKind.AwaitableObject ? new Method(this.m_fromResultMethod.MakeGenericMethod(rpcMethod.RealReturnType)) : default
};
}

View File

@@ -36,13 +36,13 @@ internal sealed class InternalRpcServerProvider : IRpcServerProvider
rpcCallContextAccessor.CallContext = callContext;
}
var ps = callContext.Parameters;
var rpcMethod = callContext.RpcMethod;
if (rpcMethod is null)
var method = callContext.RpcMethod;
if (method is null)
{
return new InvokeResult(InvokeStatus.UnFound);
}
var filters = callContext.RpcMethod.GetFilters();
var filters = method.GetFilters();
try
{
for (var i = 0; i < filters.Count; i++)
@@ -55,37 +55,15 @@ internal sealed class InternalRpcServerProvider : IRpcServerProvider
{
var rpcServer = this.GetRpcServer(callContext);
//调用
switch (callContext.RpcMethod.TaskType)
if (method.IsAwaitable)
{
case TaskReturnType.Task:
{
await ((Task)callContext.RpcMethod.Invoke(rpcServer, ps)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
}
break;
case TaskReturnType.TaskObject:
{
invokeResult.Result = await callContext.RpcMethod.InvokeObjectAsync(rpcServer, ps)
invokeResult.Result = await callContext.RpcMethod.InvokeAsync(rpcServer, ps)
.ConfigureAwait(EasyTask.ContinueOnCapturedContext);
}
break;
default:
case TaskReturnType.None:
{
if (callContext.RpcMethod.HasReturn)
{
invokeResult.Result = callContext.RpcMethod.Invoke(rpcServer, ps);
}
else
{
callContext.RpcMethod.Invoke(rpcServer, ps);
}
}
break;
}
else
{
invokeResult.Result = callContext.RpcMethod.Invoke(rpcServer, ps);
}
invokeResult.Status = InvokeStatus.Success;
}
}

View File

@@ -392,8 +392,8 @@ public sealed class SwaggerPlugin : PluginBase, IServerStartedPlugin, IHttpPlugi
openApiResponse.Content.Add("text/plain", openApiContent);
openApiResponse.Content.Add("text/json", openApiContent);
openApiResponse.Content.Add("application/xml", openApiContent);
openApiContent.Schema = this.CreateSchema(rpcMethod.ReturnType);
this.AddSchemaType(rpcMethod.ReturnType, schemaTypeList);
openApiContent.Schema = this.CreateSchema(rpcMethod.RealReturnType);
this.AddSchemaType(rpcMethod.RealReturnType, schemaTypeList);
}
openApiPathValue.Responses = new Dictionary<string, OpenApiResponse>();

View File

@@ -169,7 +169,7 @@ public sealed class WebApiAttribute : RpcAttribute
var parameterInfos = webApiParameterInfos.Where(a => a.IsFromHeader);
var list = parameterInfos.Select(a => $"new KeyValuePair<string, string>(\"{a.FromHeaderName}\",{GetParameterToString(a.Parameter)})").ToList();
if (rpcMethod.HasReturn && rpcMethod.ReturnType == typeof(string))
if (rpcMethod.HasReturn && rpcMethod.RealReturnType == typeof(string))
{
list.Add($"new KeyValuePair<string, string>(\"Accept\",\"text/plain\")");
}

View File

@@ -37,7 +37,7 @@ public abstract class SslOption
/// <param name="certificate">客户端证书</param>
/// <param name="chain">证书链</param>
/// <param name="sslPolicyErrors">SSL策略错误</param>
/// <returns>总是返回true表示接受证书</returns>
/// <returns>总是返回<see langword="true"/>,表示接受证书</returns>
private bool OnCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;

View File

@@ -41,7 +41,7 @@ public interface INatSessionClient : ITcpSession, ITcpListenableClient, IClient,
/// 移除目标客户端。
/// </summary>
/// <param name="client">要移除的TCP客户端。</param>
/// <returns>如果移除成功则返回true否则返回false。</returns>
/// <returns>如果移除成功则返回<see langword="true"/>,否则返回<see langword="false"/>。</returns>
bool RemoveTargetClient(NatTargetClient client);
/// <summary>

View File

@@ -526,10 +526,9 @@ public abstract class TcpSessionClientBase : ResolverConfigObject, ITcpSession,
/// <param name="sourceId">原始Id</param>
/// <param name="targetId">目标Id</param>
/// <returns>异步任务</returns>
protected virtual Task IdChanged(string sourceId, string targetId)
protected virtual async Task IdChanged(string sourceId, string targetId)
{
//此处无需执行任何操作,直接返回一个已完成的异步任务
return EasyTask.CompletedTask;
await this.PluginManager.RaiseAsync(typeof(IIdChangedPlugin), this.Resolver, this, new IdChangedEventArgs(sourceId,targetId)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
}
/// <summary>
@@ -620,7 +619,7 @@ public abstract class TcpSessionClientBase : ResolverConfigObject, ITcpSession,
/// </summary>
/// <param name="id">客户端的唯一标识符</param>
/// <param name="sessionClient">输出参数,用于返回找到的客户端实例</param>
/// <returns>如果找到对应的客户端则返回true否则返回false</returns>
/// <returns>如果找到对应的客户端,则返回<see langword="true"/>;否则返回<see langword="false"/></returns>
protected bool ProtectedTryGetClient(string id, out TcpSessionClientBase sessionClient)
{
// 调用内部方法m_tryGet来尝试获取客户端

View File

@@ -50,7 +50,7 @@ public struct UdpFrame
/// 解析给定的只读字节跨度数据。
/// </summary>
/// <param name="span">待解析的只读字节跨度。</param>
/// <returns>如果解析成功则返回true否则返回false。</returns>
/// <returns>如果解析成功,则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
public unsafe bool Parse(ReadOnlySpan<byte> span)
{
// 获取输入跨度的长度
@@ -87,10 +87,10 @@ public struct UdpFrame
}
}
// 成功解析输入跨度数据返回true
// 成功解析输入跨度数据,返回<see langword="true"/>
return true;
}
// 输入长度不足无法解析返回false
// 输入长度不足,无法解析,返回<see langword="false"/>
return false;
}
}

View File

@@ -133,7 +133,7 @@ public static class ServiceExtension
/// <param name="connectableService">一个实现了<see cref="IConnectableService{TClient}"/>接口的可连接服务对象。</param>
/// <param name="id">要获取的客户端的唯一标识符。</param>
/// <param name="client">如果找到匹配的客户端则设置此参数为该客户端对象如果未找到则设置为default(TClient)。</param>
/// <returns>如果找到匹配的客户端则返回true否则返回false。</returns>
/// <returns>如果找到匹配的客户端,则返回<see langword="true"/>;否则返回<see langword="false"/>。</returns>
public static bool TryGetClient<TClient>(this IConnectableService<TClient> connectableService, string id, out TClient client)
where TClient : IIdClient, IClient
{

View File

@@ -95,7 +95,7 @@ public static class SocketPluginManagerExtension
/// </summary>
/// <param name="pluginManager"></param>
/// <param name="sleepTime">失败时间隔时间</param>
/// <param name="failCallback">失败时回调参数依次为客户端本轮尝试重连次数异常信息。如果回调为null或者返回false则终止尝试下次连接。</param>
/// <param name="failCallback">失败时回调参数依次为客户端本轮尝试重连次数异常信息。如果回调为null或者返回<see langword="false"/>,则终止尝试下次连接。</param>
/// <param name="successCallback">成功连接时回调。</param>
/// <returns></returns>
[Obsolete("此配置已被弃用请使用UseTcpReconnection代替", true)]

View File

@@ -42,7 +42,7 @@ public interface IClientCollection<TClient> : IEnumerable<TClient> where TClient
/// 判断指定Id的客户端是否存在于集合中
/// </summary>
/// <param name="id">要查找的客户端的唯一标识符</param>
/// <returns>如果集合中存在该Id对应的客户端返回true否则返回false</returns>
/// <returns>如果集合中存在该Id对应的客户端返回<see langword="true"/>,否则返回<see langword="false"/></returns>
bool ClientExist(string id);
/// <summary>
@@ -50,6 +50,6 @@ public interface IClientCollection<TClient> : IEnumerable<TClient> where TClient
/// </summary>
/// <param name="id">要获取的客户端的唯一标识符</param>
/// <param name="client">输出参数,用于存储找到的客户端对象</param>
/// <returns>如果找到对应的客户端对象返回true否则返回false</returns>
/// <returns>如果找到对应的客户端对象返回<see langword="true"/>,否则返回<see langword="false"/></returns>
bool TryGetClient(string id, out TClient client);
}

View File

@@ -46,7 +46,7 @@ public abstract class ReconnectionPlugin<TClient> : PluginBase, ILoadedConfigPlu
}
/// <summary>
/// 每个周期可执行的委托。用于检验客户端活性。返回true表示存活返回
/// 每个周期可执行的委托。用于检验客户端活性。返回<see langword="true"/>表示存活,返回
/// </summary>
public abstract Func<TClient, int, Task<bool?>> ActionForCheck { get; set; }
@@ -113,7 +113,7 @@ public abstract class ReconnectionPlugin<TClient> : PluginBase, ILoadedConfigPlu
/// 设置连接动作
/// </summary>
/// <param name="sleepTime">失败时间隔时间</param>
/// <param name="failCallback">失败时回调参数依次为客户端本轮尝试重连次数异常信息。如果回调为null或者返回false则终止尝试下次连接。</param>
/// <param name="failCallback">失败时回调参数依次为客户端本轮尝试重连次数异常信息。如果回调为null或者返回<see langword="false"/>,则终止尝试下次连接。</param>
/// <param name="successCallback">成功连接时回调</param>
/// <returns></returns>
public ReconnectionPlugin<TClient> SetConnectAction(TimeSpan sleepTime,

View File

@@ -106,23 +106,7 @@ public abstract class TcpCommandLinePlugin : PluginBase, ITcpReceivedPlugin
try
{
object result;
switch (method.TaskType)
{
case TaskReturnType.Task:
await method.InvokeAsync(this, os).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
result = default;
break;
case TaskReturnType.TaskObject:
result = await method.InvokeObjectAsync(this, os).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
break;
case TaskReturnType.None:
default:
result = method.Invoke(this, os);
break;
}
var result=await method.InvokeAsync(this, os).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
if (method.HasReturn)
{
await clientSender.SendAsync(this.Converter.Serialize(null, result)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);