Browse Source

Apewer-6.7.6

dev
王厅 1 year ago
parent
commit
bd51f72b5c
  1. 3
      Apewer.Windows/Internals/Interop/Kernel32.cs
  2. 3
      Apewer.Windows/Internals/Interop/User32.cs
  3. 49
      Apewer.Windows/Surface/Extensions.cs
  4. 158
      Apewer.Windows/Surface/FormsUtility.cs
  5. 23
      Apewer.Windows/Surface/ImageUtility.cs
  6. 21
      Apewer.Windows/_Extensions.cs
  7. 2
      Apewer/Apewer.props
  8. 328
      Apewer/BytesSet.cs
  9. 191
      Apewer/BytesUtility.cs
  10. 12
      Apewer/CipherSet.cs
  11. 70
      Apewer/CollectionUtility.cs
  12. 365
      Apewer/Json.cs
  13. 2
      Apewer/Network/Extension.cs
  14. 310
      Apewer/NumberUtility.cs
  15. 71
      Apewer/RuntimeUtility.cs
  16. 51
      Apewer/Source/SourceUtility.cs
  17. 30
      Apewer/Web/ApiUtility.cs
  18. 26
      Apewer/_Extensions.cs
  19. 13
      ChangeLog.md

3
Apewer.Windows/Internals/Interop/Kernel32.cs

@ -43,6 +43,9 @@ namespace Apewer.Internals.Interop
[DllImport("kernel32.dll", ExactSpelling = true)] [DllImport("kernel32.dll", ExactSpelling = true)]
public static extern IntPtr GetCurrentProcess(); public static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern int GetCurrentThreadId();
[DllImport("kernel32")] [DllImport("kernel32")]
public static extern int GetShortPathName(string lpszLongPath, string lpszShortPath, int cchBuffer); public static extern int GetShortPathName(string lpszLongPath, string lpszShortPath, int cchBuffer);

3
Apewer.Windows/Internals/Interop/User32.cs

@ -125,6 +125,9 @@ namespace Apewer.Internals.Interop
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern int GetWindowTextW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder lpString, int nMaxCount); public static extern int GetWindowTextW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern int GetWindowThreadProcessId(HandleRef hWnd, out int lpdwProcessId);
/// <summary></summary> /// <summary></summary>
/// <param name="hWnd"></param> /// <param name="hWnd"></param>
/// <returns></returns> /// <returns></returns>

49
Apewer.Windows/Surface/Extensions.cs

@ -1,49 +0,0 @@
#if NET40 || NET461
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Apewer.Surface
{
/// <summary></summary>
public static class Extensions
{
/// <summary>保存为 PNG 文件。</summary>
public static byte[] SaveAsPng(this Image @this, bool disposeImage = false)
{
return ImageUtility.SaveAsBytes(@this, ImageFormat.Png, disposeImage);
}
/// <summary>保存为 JPEG 文件。</summary>
public static byte[] SaveAsJpeg(this Image @this, bool disposeImage = false)
{
return ImageUtility.SaveAsBytes(@this, ImageFormat.Jpeg, disposeImage);
}
/// <summary></summary>
public static void BeginInvoke(this Control control, Action action)
{
if (action == null) return;
control.BeginInvoke(action as Delegate);
}
/// <summary></summary>
public static void Invoke(this Control control, Action action)
{
if (action == null) return;
control.Invoke(action as Delegate);
}
}
}
#endif

158
Apewer.Windows/Surface/FormsUtility.cs

@ -23,7 +23,7 @@ namespace Apewer.Surface
/// <summary>窗体实用工具。</summary> /// <summary>窗体实用工具。</summary>
[SecuritySafeCritical] [SecuritySafeCritical]
public class FormsUtility public static class FormsUtility
{ {
/// <summary>线程锁。</summary> /// <summary>线程锁。</summary>
@ -76,6 +76,50 @@ namespace Apewer.Surface
#endif #endif
#region 线程
/// <summary>在拥有此控件的基础窗口句柄的线程上执行指定的委托。</summary>
public static void Invoke(this Control control, Action action)
{
if (control == null) throw new ArgumentNullException(nameof(control));
if (action == null) throw new ArgumentNullException(nameof(action));
// control.Invoke(action as Delegate);
control.Invoke(new Action(delegate ()
{
action.Invoke();
}));
}
/// <summary>在创建控件的基础句柄所在线程上异步执行指定委托。</summary>
/// <exception cref="ArgumentNullException" />
public static IAsyncResult BeginInvoke(this Control control, Action action)
{
if (control == null) throw new ArgumentNullException(nameof(control));
if (action == null) throw new ArgumentNullException(nameof(action));
// control.BeginInvoke(action as Delegate);
return control.BeginInvoke(new Action(delegate ()
{
action.Invoke();
}));
}
/// <summary>控件属于当前线程。</summary>
/// <exception cref="ArgumentNullException" />
public static bool OnCurrentThread(Control control)
{
if (control == null) throw new ArgumentNullException(nameof(control));
var controlThreadId = User32.GetWindowThreadProcessId(new HandleRef(control, control.Handle), out int _);
var currentThreadId = Kernel32.GetCurrentThreadId();
return controlThreadId == currentThreadId;
}
#endregion
#region 颜色。 #region 颜色。
/// <summary>获取所有可枚举的颜色。</summary> /// <summary>获取所有可枚举的颜色。</summary>
@ -603,38 +647,6 @@ namespace Apewer.Surface
current.Visible = false; current.Visible = false;
} }
/// <summary></summary>
public static void Invoke(Control control, Action action, bool async = false)
{
if (control == null || action == null) return;
// control.Invoke(action);
if (async)
{
control.BeginInvoke(new Action(delegate ()
{
action.Invoke();
}));
}
else
{
control.Invoke(new Action(delegate ()
{
action.Invoke();
}));
}
}
/// <summary></summary>
public static void BeginInvoke(Control control, Action action)
{
if (control == null || action == null) return;
control.BeginInvoke(new Action(delegate ()
{
action.Invoke();
}));
}
/// <summary>设置窗体置顶。</summary> /// <summary>设置窗体置顶。</summary>
public static void SetTopMost(IntPtr form, bool value = true) public static void SetTopMost(IntPtr form, bool value = true)
{ {
@ -1041,6 +1053,86 @@ namespace Apewer.Surface
#endregion #endregion
#region WndProc
/// <summary>允许鼠标调整窗体大小。此方法对 FormBorderStyle 为 None 的窗体生效。</summary>
/// <returns>已处理事件。</returns>
/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentOutOfRangeException" />
public static bool AllowResizeForm(this Form form, ref Message m, int padding = 4)
{
if (form == null) throw new ArgumentNullException(nameof(form));
if (form.FormBorderStyle != FormBorderStyle.None) return false;
if (padding < 0) throw new ArgumentOutOfRangeException(nameof(padding));
const int HT_LEFT = 10;
const int HT_RIGHT = 11;
const int HT_TOP = 12;
const int HT_TOP_LEFT = 13;
const int HT_TOP_RIGHT = 14;
const int HT_BOTTOM = 15;
const int HT_BOTTOM_LEFT = 16;
const int HT_BOTTOM_RIGHT = 17;
switch (m.Msg)
{
case 0x0084:
var clientSize = form.ClientSize;
var screenPoint = new System.Drawing.Point((int)m.LParam & 0xFFFF, (int)m.LParam >> 16 & 0xFFFF);
var point = form.PointToClient(screenPoint);
if (point.X <= padding)
{
if (point.Y <= padding) m.Result = (IntPtr)HT_TOP_LEFT;
else if (point.Y >= clientSize.Height - padding) m.Result = (IntPtr)HT_BOTTOM_LEFT;
else m.Result = (IntPtr)HT_LEFT;
return true;
}
else if (point.X >= clientSize.Width - padding)
{
if (point.Y <= padding) m.Result = (IntPtr)HT_TOP_RIGHT;
else if (point.Y >= clientSize.Height - padding) m.Result = (IntPtr)HT_BOTTOM_RIGHT;
else m.Result = (IntPtr)HT_RIGHT;
return true;
}
else if (point.Y <= padding)
{
m.Result = (IntPtr)HT_TOP;
return true;
}
else if (point.Y >= clientSize.Height - padding)
{
m.Result = (IntPtr)HT_BOTTOM;
return true;
}
break;
}
return false;
}
/// <summary>允许移动窗体。左键点击时修改消息,认为鼠标点在非客户区(标题栏)。</summary>
/// <returns>已处理事件。</returns>
/// <exception cref="ArgumentNullException" />
public static bool AllowMoveForm(this Form form, ref Message m)
{
if (form == null) throw new ArgumentNullException(nameof(form));
switch (m.Msg)
{
// 左键点击时修改消息,认为鼠标点在非客户区(标题栏)。
case 0x0201:
m.Msg = 0x00A1;
m.LParam = IntPtr.Zero;
m.WParam = new IntPtr(2);
return true;
}
return false;
}
#endregion
} }
} }

23
Apewer.Windows/Surface/ImageUtility.cs

@ -21,10 +21,12 @@ namespace Apewer.Surface
internal static byte[] EmptyBytes { get { return new byte[0]; } } internal static byte[] EmptyBytes { get { return new byte[0]; } }
/// <summary>保存图像到文件,失败时返回 NULL 值。</summary> /// <summary>保存图像到文件。</summary>
public static byte[] SaveAsBytes(Image image, ImageFormat format, bool dispose = false) /// <exception cref="ArgumentNullException" />
public static byte[] SaveAs(this Image image, ImageFormat format)
{ {
if (image == null) return EmptyBytes; if (image == null) throw new ArgumentNullException(nameof(image));
if (format == null) throw new ArgumentNullException(nameof(format));
var memory = new MemoryStream(); var memory = new MemoryStream();
var bytes = null as byte[]; var bytes = null as byte[];
@ -35,20 +37,21 @@ namespace Apewer.Surface
} }
catch { } catch { }
memory.Dispose(); memory.Dispose();
if (dispose) image.Dispose();
return bytes; return bytes;
} }
/// <summary>保存为 PNG 文件,失败时返回 NULL 值。</summary> /// <summary>保存为 PNG 文件。</summary>
public static byte[] SaveAsPng(Image image, bool dispose = false) /// <exception cref="ArgumentNullException" />
public static byte[] SaveAsPng(this Image image)
{ {
return SaveAsBytes(image, ImageFormat.Png, dispose); return SaveAs(image, ImageFormat.Png);
} }
/// <summary>保存为 JPEG 文件,失败时返回 NULL 值。</summary> /// <summary>保存为 JPEG 文件。</summary>
public static byte[] SaveAsJpeg(Image image, bool dispose = false) /// <exception cref="ArgumentNullException" />
public static byte[] SaveAsJpeg(this Image image)
{ {
return SaveAsBytes(image, ImageFormat.Jpeg, dispose); return SaveAs(image, ImageFormat.Jpeg);
} }
/// <summary>调整图像尺寸,生成新图像。</summary> /// <summary>调整图像尺寸,生成新图像。</summary>

21
Apewer.Windows/_Extensions.cs

@ -1,21 +0,0 @@
using Apewer;
using Apewer.Surface;
using System;
using System.Windows.Forms;
/// <summary>扩展方法。</summary>
public static class Extensions_Apewer_Windows
{
#region Surface
#if NETFX || NETCORE
/// <summary></summary>
public static void Invoke(this Control @this, Action action, bool async = false) => FormsUtility.Invoke(@this, action, async);
#endif
#endregion
}

2
Apewer/Apewer.props

@ -9,7 +9,7 @@
<Description></Description> <Description></Description>
<RootNamespace>Apewer</RootNamespace> <RootNamespace>Apewer</RootNamespace>
<Product>Apewer Libraries</Product> <Product>Apewer Libraries</Product>
<Version>6.7.5</Version> <Version>6.7.6</Version>
</PropertyGroup> </PropertyGroup>
<!-- 生成 --> <!-- 生成 -->

328
Apewer/BytesSet.cs

@ -1,328 +0,0 @@
using Apewer.Internals;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Apewer
{
/// <summary></summary>
public class BytesSet
{
private Dictionary<string, byte[]> _dict = new Dictionary<string, byte[]>();
private volatile Func<byte[], byte[]> _inbound = null;
private volatile Func<byte[], byte[]> _outbound = null;
/// <summary></summary>
public byte[] this[string key]
{
get { return GetValue(key); }
set { SetValue(key, value); }
}
/// <summary>设置或获取值入站的函数,设置时字典必须为空。设置为 Null 将忽略入站函数。</summary>
public Func<byte[], byte[]> Inbound
{
get { lock (_dict) return _inbound; }
set { lock (_dict) _inbound = value; }
}
/// <summary>设置或获取值出站的函数。设置为 Null 将忽略出站函数。</summary>
public Func<byte[], byte[]> Outbound
{
get { lock (_dict) return _outbound; }
set { lock (_dict) _outbound = value; }
}
/// <summary></summary>
public int Count
{
get
{
var count = 0;
lock (_dict) { count = _dict.Count; }
return count;
}
}
/// <summary></summary>
public List<string> Keys
{
get
{
var list = new List<string>();
lock (_dict)
{
list.AddRange(_dict.Keys);
}
return list;
}
}
/// <summary></summary>
public BytesSet() { }
/// <summary></summary>
public BytesSet(Func<byte[], byte[]> inbound, Func<byte[], byte[]> outbound)
{
_inbound = inbound;
_outbound = outbound;
}
/// <summary></summary>
public void Clear()
{
lock (_dict)
{
foreach (var key in _dict.Keys)
{
_dict[key] = null;
}
_dict.Clear();
}
}
/// <summary></summary>
public bool Import(byte[] data)
{
var memory = new MemoryStream();
lock (data)
{
if (data != null) memory.Write(data, 0, data.Length);
BytesUtility.ResetPosition(memory);
}
if (memory.Length < 4) return false;
var count = 0;
var first = true;
while (true)
{
if (!CanRead(memory, 4)) break;
if (first)
{
var buffer = new byte[4];
memory.Read(buffer, 0, 4);
count = GetInt32(buffer);
first = false;
}
else
{
// Read Key Length
var keylength = 0;
{
if (!CanRead(memory, 4)) break;
var buffer = new byte[4];
memory.Read(buffer, 0, 4);
keylength = GetInt32(buffer);
}
// Read Key Data
var key = Constant.EmptyString;
if (keylength > 1)
{
if (!CanRead(memory, keylength)) break;
var buffer = new byte[keylength];
memory.Read(buffer, 0, keylength);
key = BytesUtility.ToText(buffer);
}
// Read Value Length
var valuelength = 0;
{
if (!CanRead(memory, 4)) break;
var buffer = new byte[4];
memory.Read(buffer, 0, 4);
valuelength = GetInt32(buffer);
}
// Read Key Data
var value = Constant.EmptyBytes;
if (valuelength > 1)
{
if (!CanRead(memory, valuelength)) break;
var buffer = new byte[valuelength];
memory.Read(buffer, 0, valuelength);
value = BytesUtility.Clone(buffer);
}
if (_dict.ContainsKey(key)) continue;
_dict.Add(key, value);
if (_dict.Count >= count) break;
}
}
return count == _dict.Count;
}
/// <summary></summary>
public byte[] Export()
{
var memory = new MemoryStream();
lock (_dict)
{
var count = _dict.Count;
var countbytes = GetBytes(count);
memory.Write(countbytes, 0, countbytes.Length);
foreach (var pair in _dict)
{
var keydata = BytesUtility.FromText(pair.Key);
var keycount = GetBytes(keydata.Length);
memory.Write(keycount, 0, keycount.Length);
if (keydata.Length > 0) memory.Write(keydata, 0, keydata.Length);
var valuedata = pair.Value ?? Constant.EmptyBytes;
var valuecount = GetBytes(valuedata.Length);
memory.Write(valuecount, 0, valuecount.Length);
if (valuedata.Length > 0) memory.Write(valuedata, 0, valuedata.Length);
}
}
var data = memory.ToArray();
memory.Dispose();
return data;
}
/// <summary></summary>
public bool Contains(string key)
{
if (key == null) return false;
var contains = false;
lock (_dict)
{
contains = _dict.ContainsKey(key);
}
return contains;
}
/// <summary></summary>
public byte[] GetValue(string key)
{
var k = key;
var v = Constant.EmptyBytes;
if (k == null) return v;
lock (_dict)
{
if (_dict.ContainsKey(k)) v = _dict[k];
}
if (_outbound != null)
{
v = _outbound(v);
if (v == null) v = Constant.EmptyBytes;
}
return v;
}
/// <summary></summary>
public bool SetValue(string key, byte[] value)
{
var k = key;
var v = Constant.EmptyBytes;
if (k == null) return false;
lock (value)
{
if (value != null) v = BytesUtility.Clone(value);
}
if (_inbound != null)
{
v = _inbound(v);
if (v == null) v = Constant.EmptyBytes;
}
lock (_dict)
{
if (_dict.ContainsKey(k)) _dict.Remove(k);
_dict.Add(k, v);
}
return true;
}
private static bool CanRead(Stream stream, int length)
{
if (length < 0) return false;
if (stream == null) return false;
if (stream.CanRead == false) return false;
if (stream.Position + length > stream.Length) return false;
return true;
}
/// <summary>Int32 -> Byte[]</summary>
private static byte[] GetBytes(int value)
{
const int t3 = 256 * 256 * 256;
const int t2 = 256 * 256;
const int t1 = 256;
byte[] bs = { 0, 0, 0, 0 };
if (value >= 0)
{
int vint = value;
bs[0] = (byte)(vint / t3);
vint = vint % t3;
bs[1] = (byte)(vint / t2);
vint = vint % t2;
bs[2] = (byte)(vint / t1);
vint = vint % t1;
bs[3] = (byte)(vint);
}
else
{
int minusInt = Math.Abs(value + 1);
var minusBytes = GetBytes(minusInt);
bs[0] = (byte)(255 - minusBytes[0]);
bs[1] = (byte)(255 - minusBytes[1]);
bs[2] = (byte)(255 - minusBytes[2]);
bs[3] = (byte)(255 - minusBytes[3]);
}
return bs;
}
/// <summary>Byte[] -> Int32</summary>
private static Int32 GetInt32(byte[] value)
{
if (value.Length == 4)
{
const int t3 = 256 * 256 * 256;
const int t2 = 256 * 256;
const int t1 = 256;
if (value[0] <= 127)
{
int[] vis = { 0, 0, 0, 0 };
vis[0] = value[0] * t3;
vis[1] = value[1] * t2;
vis[2] = value[2] * t1;
vis[3] = value[3];
int vr = vis[0] + vis[1] + vis[2] + vis[3];
return vr;
}
else
{
if ((value[0] == 128) && (value[1] == 0) && (value[2] == 0) && (value[3] == 0))
{
return int.MinValue;
}
else
{
var bytes = new byte[4];
bytes[0] = (byte)(255 - value[0]);
bytes[1] = (byte)(255 - value[1]);
bytes[2] = (byte)(255 - value[2]);
bytes[3] = (byte)(255 - value[3]);
int vminusint = 0 - 1 - GetInt32(bytes);
return vminusint;
}
}
}
return 0;
}
}
}

191
Apewer/BytesUtility.cs

@ -13,7 +13,7 @@ namespace Apewer
{ {
/// <summary>二进制。</summary> /// <summary>二进制。</summary>
public class BytesUtility public static class BytesUtility
{ {
/// <summary>空字节数组,每次获取都将创建新的引用。</summary> /// <summary>空字节数组,每次获取都将创建新的引用。</summary>
@ -42,25 +42,19 @@ namespace Apewer
#region Bytes Array #region Bytes Array
/// <summary>克隆字节数组。当源为 NULL 时获取零元素字节数组。</summary> /// <summary>克隆字节数组。当源为 NULL 时获取零元素字节数组。</summary>
public static byte[] Clone(byte[] bytes) public static byte[] Clone(this byte[] bytes)
{ {
if (bytes == null || bytes.LongLength < 0L) return Empty; if (bytes == null) return Empty;
var result = new byte[bytes.LongLength]; var length = bytes.Length;
bytes.CopyTo(result, 0L); if (length < 1) return Empty;
var result = new byte[length];
Buffer.BlockCopy(bytes, 0, result, 0, length);
return result; return result;
} }
/// <summary>创建数组,元素值为零。</summary> /// <summary>每个字节取反。</summary>
public static byte[] ZeroArray(int length = 0) /// <remarks>value = 255 - value</remarks>
{ public static byte[] Adverse(this byte[] bytes)
if (length < 1) return new byte[0];
var array = new byte[length];
for (int i = 0; i < length; i++) array[i] = 0;
return array;
}
/// <summary>所有字节取反。</summary>
public static byte[] Adverse(byte[] bytes)
{ {
if (bytes == null || bytes.LongLength < 1L) return Empty; if (bytes == null || bytes.LongLength < 1L) return Empty;
var adverse = new byte[bytes.LongLength]; var adverse = new byte[bytes.LongLength];
@ -69,41 +63,42 @@ namespace Apewer
} }
/// <summary>确定此字节数组实例的开头是否与指定的字节数组匹配。</summary> /// <summary>确定此字节数组实例的开头是否与指定的字节数组匹配。</summary>
public static bool StartsWith(byte[] bytes, params byte[] head) public static bool StartsWith(this byte[] bytes, params byte[] head)
{ {
var data = bytes; // 头部为空,视为匹配。
if (head == null) return true;
var length = head.Length;
if (length < 1) return true;
if (data == null) return false; // 样本为空,视为不匹配。
if (head == null) return false; if (bytes == null) return false;
if (bytes.Length < length) return false;
var datalength = data.LongLength;
var headlength = head.LongLength;
if (datalength < headlength) return false;
if (headlength < 1L) return true;
for (long i = 0; i < head.LongLength; i++) // 对比头部字节。
for (var i = 0; i < length; i++)
{ {
if (data[i] != head[i]) return false; if (bytes[i] != head[i]) return false;
} }
return true; return true;
} }
/// <summary>确定此字节数组实例的结尾是否与指定的字节数组匹配。</summary> /// <summary>确定此字节数组实例的结尾是否与指定的字节数组匹配。</summary>
public static bool EndsWith(byte[] bytes, params byte[] end) public static bool EndsWith(this byte[] bytes, params byte[] foot)
{ {
if (bytes == null) return false; // 尾部为空,视为匹配。
if (end == null) return false; if (foot == null) return true;
var length = foot.Length;
if (length < 1) return true;
var dataLength = bytes.LongLength; // 样本为空,视为不匹配。
var endLength = end.LongLength; if (bytes == null) return false;
if (dataLength < endLength) return false; if (bytes.Length < length) return false;
if (endLength < 1L) return true;
for (long i = 0; i < endLength; i++) // 对比尾部字节。
var offset = bytes.Length - length;
for (var i = 0; i < length; i++)
{ {
var dataindex = dataLength - i - 1; if (bytes[offset + i] != foot[i]) return false;
var headindex = endLength - i - 1;
if (bytes[dataindex] != end[headindex]) return false;
} }
return true; return true;
} }
@ -142,7 +137,7 @@ namespace Apewer
public static byte[] Append(byte[] head, params byte[] bytes) => Merge(head, bytes); public static byte[] Append(byte[] head, params byte[] bytes) => Merge(head, bytes);
/// <summary>为文本数据添加 BOM 字节,若已存在则忽略。</summary> /// <summary>为文本数据添加 BOM 字节,若已存在则忽略。</summary>
public static byte[] AddTextBom(params byte[] bytes) public static byte[] AddTextBom(this byte[] bytes)
{ {
var bom = new byte[] { 0xEF, 0xBB, 0xBF }; var bom = new byte[] { 0xEF, 0xBB, 0xBF };
if (bytes == null || bytes.LongLength < 1L) return bom; if (bytes == null || bytes.LongLength < 1L) return bom;
@ -152,7 +147,7 @@ namespace Apewer
} }
/// <summary>去除文本数据的 BOM 字节,若不存在则忽略。</summary> /// <summary>去除文本数据的 BOM 字节,若不存在则忽略。</summary>
public static byte[] WipeTextBom(byte[] bytes) public static byte[] WipeTextBom(this byte[] bytes)
{ {
if (bytes == null) return Empty; if (bytes == null) return Empty;
var hasBom = (bytes.Length >= 3) && (bytes[0] == 0xEF) && (bytes[1] == 0xBB) && (bytes[2] == 0xBF); var hasBom = (bytes.Length >= 3) && (bytes[0] == 0xEF) && (bytes[1] == 0xBB) && (bytes[2] == 0xBF);
@ -171,7 +166,7 @@ namespace Apewer
#region Text #region Text
/// <summary>将字节数组转换为十六进制文本。</summary> /// <summary>将字节数组转换为十六进制文本。</summary>
public static string ToHex(params byte[] bytes) public static string ToHex(this byte[] bytes)
{ {
int length = bytes.Length; int length = bytes.Length;
if (length > 0) if (length > 0)
@ -188,7 +183,7 @@ namespace Apewer
} }
/// <summary>将十六进制文本转换为字节数组。</summary> /// <summary>将十六进制文本转换为字节数组。</summary>
public static byte[] FromHex(string hex) public static byte[] FromHex(this string hex)
{ {
if (string.IsNullOrEmpty(hex) || hex.Length < 2) return Empty; if (string.IsNullOrEmpty(hex) || hex.Length < 2) return Empty;
if (hex.Length % 2 != 0) return Empty; if (hex.Length % 2 != 0) return Empty;
@ -208,11 +203,9 @@ namespace Apewer
return bytes; return bytes;
} }
/// <summary>将字节数组格式化为大写十六进制字符串。</summary>
public static string ToX2(params byte[] bytes) => ToX2(true, bytes);
/// <summary>将字节数组格式化为十六进制字符串,可指定大小写。</summary> /// <summary>将字节数组格式化为十六进制字符串,可指定大小写。</summary>
public static string ToX2(bool upper, params byte[] bytes) /// <remarks>例:D41D8CD98F00B204E9800998ECF8427E</remarks>
public static string ToX2(this byte[] bytes, bool upper = true)
{ {
if (bytes == null) return ""; if (bytes == null) return "";
var length = bytes.Length; var length = bytes.Length;
@ -233,7 +226,7 @@ namespace Apewer
/// <summary>Byte[] -> Base64</summary> /// <summary>Byte[] -> Base64</summary>
public static string ToBase64(params byte[] bytes) public static string ToBase64(params byte[] bytes)
{ {
if (bytes.Length < 1) return Constant.EmptyString; if (bytes == null || bytes.Length < 1) return Constant.EmptyString;
try { return Convert.ToBase64String(bytes); } try { return Convert.ToBase64String(bytes); }
catch { return Constant.EmptyString; } catch { return Constant.EmptyString; }
} }
@ -859,123 +852,97 @@ namespace Apewer
{ {
try try
{ {
var algorithm = new T(); using (var algorithm = new T())
var result = algorithm.ComputeHash(bytes); {
algorithm.Clear(); var result = algorithm.ComputeHash(bytes);
#if !NET20 algorithm.Clear();
algorithm.Dispose(); return result;
#endif }
return result;
} }
catch { } catch { }
} }
return Empty; return Empty;
} }
private static byte[] ComputeHash<T>(Stream stream, bool dispose, Action<long> progress) where T : HashAlgorithm, new() private static byte[] ComputeHash<T>(Stream stream, Action<long> progress) where T : HashAlgorithm, new()
{ {
if (progress == null) if (progress == null)
{ {
if (stream != null)
using (var algorithm = new T())
{ {
var result = Empty; if (stream == null)
try
{ {
var algorithm = new T(); var result = algorithm.ComputeHash(Empty);
result = algorithm.ComputeHash(stream);
algorithm.Clear(); algorithm.Clear();
#if !NET20 return result;
algorithm.Dispose(); }
#endif else
{
var result = algorithm.ComputeHash(stream);
algorithm.Clear();
return result;
} }
catch { }
if (dispose) stream.Dispose();
return result;
} }
return Empty;
} }
else else
{ {
if (stream == null) return Empty; if (stream == null) return Empty;
// 初始化。 // 初始化。
var validcallback = progress != null; using (var algorithm = new T())
var capacity = DefaultBuffer;
var buffer = new byte[capacity];
var algorithm = new T();
algorithm.Initialize();
// 读取。
var failed = false;
while (true)
{ {
var read = 0; algorithm.Initialize();
try { read = stream.Read(buffer, 0, capacity); }
catch { failed = true; }
if (read < capacity) // 读取。
var count = 0;
var input = new byte[DefaultBuffer];
var output = new byte[DefaultBuffer];
while (true)
{ {
if (read < 1) count = stream.Read(input, 0, DefaultBuffer);
if (count < DefaultBuffer)
{ {
algorithm.TransformFinalBlock(new byte[0], 0, 0); algorithm.TransformFinalBlock(input, 0, count);
break;
} }
else else
{ {
algorithm.TransformFinalBlock(buffer, 0, Convert.ToInt32(read)); algorithm.TransformBlock(input, 0, count, output, 0);
} }
break;
} }
else
{
algorithm.TransformBlock(buffer, 0, Convert.ToInt32(read), buffer, 0);
}
}
if (failed)
{
algorithm.Clear();
#if !NET20
algorithm.Dispose();
#endif
if (dispose) stream.Dispose();
return Empty;
}
else
{
var result = algorithm.Hash; var result = algorithm.Hash;
algorithm.Clear(); algorithm.Clear();
#if !NET20
algorithm.Dispose();
#endif
if (dispose) stream.Dispose();
return result; return result;
} }
} }
} }
/// <summary>获取 MD5 值。</summary> /// <summary>获取 MD5 值。</summary>
public static byte[] MD5(params byte[] bytes) => ComputeHash<MD5CryptoServiceProvider>(bytes); public static byte[] MD5(this byte[] bytes) => ComputeHash<MD5CryptoServiceProvider>(bytes);
/// <summary>获取 MD5 值。</summary> /// <summary>获取 MD5 值。</summary>
public static byte[] MD5(Stream stream, Action<long> progress = null) => ComputeHash<MD5CryptoServiceProvider>(stream, false, progress); public static byte[] MD5(this Stream stream, Action<long> progress = null) => ComputeHash<MD5CryptoServiceProvider>(stream, progress);
/// <summary>获取 SHA1 值。</summary> /// <summary>获取 SHA1 值。</summary>
public static byte[] SHA1(params byte[] bytes) => ComputeHash<SHA1CryptoServiceProvider>(bytes); public static byte[] SHA1(this byte[] bytes) => ComputeHash<SHA1CryptoServiceProvider>(bytes);
/// <summary>获取 SHA1 值。</summary> /// <summary>获取 SHA1 值。</summary>
public static byte[] SHA1(Stream stream, Action<long> progress = null) => ComputeHash<SHA1CryptoServiceProvider>(stream, false, progress); public static byte[] SHA1(this Stream stream, Action<long> progress = null) => ComputeHash<SHA1CryptoServiceProvider>(stream, progress);
/// <summary>获取 SHA256 值。</summary> /// <summary>获取 SHA256 值。</summary>
public static byte[] SHA256(params byte[] bytes) => ComputeHash<SHA256CryptoServiceProvider>(bytes); public static byte[] SHA256(this byte[] bytes) => ComputeHash<SHA256CryptoServiceProvider>(bytes);
/// <summary>获取 SHA256 值。</summary> /// <summary>获取 SHA256 值。</summary>
public static byte[] SHA256(Stream stream, Action<long> progress = null) => ComputeHash<SHA256CryptoServiceProvider>(stream, false, progress); public static byte[] SHA256(this Stream stream, Action<long> progress = null) => ComputeHash<SHA256CryptoServiceProvider>(stream, progress);
/// <summary>获取 SHA512 值。</summary> /// <summary>获取 SHA512 值。</summary>
public static byte[] SHA512(params byte[] bytes) => ComputeHash<SHA512CryptoServiceProvider>(bytes); public static byte[] SHA512(this byte[] bytes) => ComputeHash<SHA512CryptoServiceProvider>(bytes);
/// <summary>获取 SHA512 值。</summary> /// <summary>获取 SHA512 值。</summary>
public static byte[] SHA512(Stream stream, Action<long> progress = null) => ComputeHash<SHA512CryptoServiceProvider>(stream, false, progress); public static byte[] SHA512(this Stream stream, Action<long> progress = null) => ComputeHash<SHA512CryptoServiceProvider>(stream, progress);
#endregion #endregion

12
Apewer/CipherSet.cs

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Apewer
{
internal class CipherSet
{
}
}

70
Apewer/CollectionUtility.cs

@ -396,6 +396,76 @@ namespace Apewer
return ab.Export(); return ab.Export();
} }
/// <summary>在数组尾部增加元素,生成新数组,不修改原数组。</summary>
/// <returns>增加元素后的新数组。</returns>
public static T[] Push<T>(this T[] array, T item)
{
if (array == null) return new T[] { item };
var length = array.Length;
var newArray = new T[length + 1];
if (length > 0) System.Array.Copy(array, 0, newArray, 0, length);
newArray[length] = item;
return newArray;
}
/// <summary>在数组尾部增加元素,生成新数组,不修改原数组。</summary>
/// <returns>增加元素后的新数组。</returns>
public static T[] Push<T>(this T[] array, T[] items)
{
if (array == null) return items ?? new T[0];
if (items == null)
{
var result = new T[array.Length];
array.CopyTo(result, 0);
return result;
}
else
{
var length1 = array.Length;
var length2 = items.Length;
var result = new T[length1 + length2];
if (length1 > 0) System.Array.Copy(array, 0, result, 0, length1);
if (length2 > 0) System.Array.Copy(items, 0, result, length1, length2);
return result;
}
}
/// <summary>在数组头部增加元素,生成新数组,不修改原数组。</summary>
/// <returns>增加元素后的新数组。</returns>
public static T[] Unshift<T>(this T[] array, T item)
{
if (array == null) return new T[] { item };
var length = array.Length;
var newArray = new T[length + 1];
newArray[0] = item;
if (length > 0) System.Array.Copy(array, 0, newArray, 1, length);
return newArray;
}
/// <summary>在数组头部增加元素,生成新数组,不修改原数组。</summary>
/// <returns>增加元素后的新数组。</returns>
public static T[] Unshift<T>(this T[] array, T[] items)
{
if (array == null) return items ?? new T[0];
if (items == null)
{
var result = new T[array.Length];
array.CopyTo(result, 0);
return result;
}
else
{
var length1 = array.Length;
var length2 = items.Length;
var result = new T[length1 + length2];
if (length2 > 0) System.Array.Copy(items, 0, result, 0, length2);
if (length1 > 0) System.Array.Copy(array, 0, result, length2, length1);
return result;
}
}
#endregion #endregion
#region 排序 #region 排序

365
Apewer/Json.cs

@ -50,9 +50,19 @@ namespace Apewer
#endregion #endregion
#region 构造。 #region 消息
private const string ValueIsNotSupported = "指定的值类型不受支持";
private const string IsNotJsonObject = "当前实例不是 Json 对象。";
private const string IsNotJsonArray = "当前实例不是 Json 数组。";
private const string InvalidIndex = "未指定有效的索引。";
private const string IndexLessZero = "指定的索引小于 0,无效。";
private const string IndexGraterMax = "指定的索引超出了最大值,无效。";
private const string IndexGraterCount = "指定的索引超出了数量,无效。";
#region 基础。 #endregion
#region JToken
[NonSerialized] [NonSerialized]
private JToken _jtoken = null; private JToken _jtoken = null;
@ -69,8 +79,57 @@ namespace Apewer
[NonSerialized] [NonSerialized]
private JValue _jvalue = null; private JValue _jvalue = null;
static JToken ToJToken(object value)
{
if (value == null) return new JValue(null, JTokenType.Null);
var type = value.GetType();
if (RuntimeUtility.IsNullableType(type)) value = RuntimeUtility.GetNullableValue(value);
if (value == null) return new JValue(null, JTokenType.Null);
if (value is Json json) return json?._jtoken ?? new JValue(null, JTokenType.Null);
if (value is DateTime dt) return new JValue(SerializeDateTime(dt));
if (value is string) return new JValue(value);
if (value is bool) return new JValue(value);
if (value is byte) return new JValue(value);
if (value is sbyte) return new JValue(value);
if (value is short) return new JValue(value);
if (value is ushort) return new JValue(value);
if (value is int) return new JValue(value);
if (value is uint) return new JValue(value);
if (value is long) return new JValue(value);
if (value is ulong uint64) return (uint64 > int.MaxValue) ? new JValue(uint64.ToString()) : new JValue(Convert.ToInt64(uint64));
if (value is float) return new JValue(value);
if (value is double) return new JValue(value);
if (value is decimal) return new JValue(value);
var from = From(value);
if (from != null) return from._jtoken;
throw new ArgumentException(ValueIsNotSupported);
}
static object ParseJToken(JToken jtoken)
{
if (jtoken == null) return null;
if (jtoken is JValue jvalue) return jvalue.Value;
if (jtoken is JObject) return new Json(jtoken);
if (jtoken is JArray) return new Json(jtoken);
if (jtoken is JProperty jproperty)
{
var value = jproperty.Value;
if (value == null) return null;
return ParseJToken(value);
}
throw new InvalidOperationException($"Json 类型 {jtoken.Type} 不支持解析。");
}
#endregion #endregion
#region 构造。
#region Reset #region Reset
/// <summary>重置当前对象为空。</summary> /// <summary>重置当前对象为空。</summary>
@ -190,19 +249,10 @@ namespace Apewer
#region 属性。 #region 属性。
/// <summary>用于兼容 SimpleJson 的操作。</summary> /// <summary>用于兼容 SimpleJson 的操作。</summary>
public string this[string name] public string this[string name] { get => GetProperty(name)?.ToString() ?? ""; set => SetProperty(name, value ?? ""); }
{
get /// <summary>获取或设置数组的元素。</summary>
{ public object this[int index] { get => GetItem(index); set => SetItem(index, value); }
var property = GetProperty(name);
if (property == null) return "";
return property.ToString();
}
set
{
SetProperty(name, value ?? "");
}
}
private JTokenType TokenType private JTokenType TokenType
{ {
@ -244,9 +294,7 @@ namespace Apewer
public string Type { get { return TokenType.ToString(); } } public string Type { get { return TokenType.ToString(); } }
/// <summary>实例有效。</summary> /// <summary>实例有效。</summary>
public public bool Available { get { return _jtoken != null && TokenType != JTokenType.None; } }
bool Available
{ get { return _jtoken != null && TokenType != JTokenType.None; } }
/// <summary>获取当前实例的值,当为 Json 格式时缩进。</summary> /// <summary>获取当前实例的值,当为 Json 格式时缩进。</summary>
public string Lucid { get { return ToString(true); } } public string Lucid { get { return ToString(true); } }
@ -921,6 +969,37 @@ namespace Apewer
} }
} }
/// <summary>获取数组的元素。</summary>
/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="InvalidOperationException" />
public object GetItem(int index)
{
if (!IsArray) throw new InvalidOperationException(IsNotJsonArray);
if (index < 0) throw new ArgumentOutOfRangeException(nameof(index), IndexLessZero);
var jarray = _jtoken as JArray;
if (index >= jarray.Count) throw new ArgumentOutOfRangeException(nameof(index), IndexGraterCount);
var item = jarray[index];
return ParseJToken(item);
}
/// <summary>设置数组的元素。</summary>
/// <exception cref="ArgumentException" />
/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="InvalidOperationException" />
public void SetItem(int index, object value)
{
if (!IsArray) throw new InvalidOperationException(IsNotJsonArray);
if (index < 0) throw new ArgumentOutOfRangeException(nameof(index), IndexLessZero);
var jarray = _jtoken as JArray;
while (jarray.Count < index + 1) jarray.Add(null);
var jtoken = ToJToken(value);
jarray.SetItem(index, jtoken);
}
#endregion #endregion
#region Property #region Property
@ -1817,13 +1896,17 @@ namespace Apewer
{ {
if (pt.Equals(typeof(DateTime))) if (pt.Equals(typeof(DateTime)))
{ {
try if (AllowException)
{ {
setter.Invoke(entity, new object[] { DeserializeDateTime(value as string) }); setter.Invoke(entity, new object[] { DeserializeDateTime(value) });
} }
catch (Exception exception) else
{ {
if (AllowException) throw exception; try
{
setter.Invoke(entity, new object[] { DeserializeDateTime(value) });
}
catch { }
} }
} }
else if (pt.Equals(typeof(string))) else if (pt.Equals(typeof(string)))
@ -1888,39 +1971,202 @@ namespace Apewer
/// <summary></summary> /// <summary></summary>
public override bool TryGetMember(GetMemberBinder binder, out object result) public override bool TryGetMember(GetMemberBinder binder, out object result)
{ {
var contains = false; if (!IsObject) throw new InvalidOperationException(IsNotJsonObject);
if (IsObject)
var contains = _jobject.ContainsKey(binder.Name);
if (!contains)
{ {
var property = GetProperty(binder.Name); result = null;
contains = property != null; return true;
result = contains ? property.Value : null;
return contains;
} }
result = null; var jtoken = _jobject.GetValue(binder.Name);
return contains; result = ParseJToken(jtoken);
return true;
} }
/// <summary></summary> /// <summary></summary>
public override bool TrySetMember(SetMemberBinder binder, object value) public override bool TrySetMember(SetMemberBinder binder, object value)
{ {
var name = binder.Name; if (!IsObject) throw new InvalidOperationException(IsNotJsonObject);
if (value == null) return SetProperty(name);
if (value is Json) return SetProperty(name, (Json)value);
if (value is string) return SetProperty(name, (string)value);
if (value is bool) return SetProperty(name, (bool)value);
if (value is byte) return SetProperty(name, (byte)value);
if (value is sbyte) return SetProperty(name, (sbyte)value);
if (value is short) return SetProperty(name, (short)value);
if (value is ushort) return SetProperty(name, (ushort)value);
if (value is int) return SetProperty(name, (int)value);
if (value is uint) return SetProperty(name, (uint)value);
if (value is long) return SetProperty(name, (long)value);
if (value is float) return SetProperty(name, (float)value);
if (value is double) return SetProperty(name, (double)value);
if (value is decimal) return SetProperty(name, (decimal)value);
var contains = _jobject.ContainsKey(binder.Name);
if (contains)
{
_jobject[binder.Name] = ToJToken(value);
return true;
}
else
{
_jobject.Add(binder.Name, ToJToken(value));
return true;
}
}
private Class<T> DynamicIndex<T>(object[] indexes, Func<string, T> stringCallback, Func<int, T> intCallback)
{
var index = indexes[0];
if (index != null)
{
if (IsObject)
{
if (index is string name) return new Class<T>(stringCallback.Invoke(name));
}
if (IsArray)
{
if (index is int || index is short || index is byte || index is ushort || index is sbyte)
{
var int32 = int.Parse(index.ToString());
if (int32 < 0) throw new ArgumentOutOfRangeException("index", IndexLessZero);
return new Class<T>(intCallback(int32));
}
else if (index is long int64)
{
if (int64 < 0) throw new ArgumentOutOfRangeException("index", IndexLessZero);
if (int64 > int.MaxValue) throw new ArgumentOutOfRangeException("index", IndexGraterMax);
var int32 = Convert.ToInt32(int64);
return new Class<T>(intCallback(int32));
}
else if (index is uint uint32)
{
if (uint32 > int.MaxValue) throw new ArgumentOutOfRangeException("index", IndexGraterMax);
var int32 = Convert.ToInt32(uint32);
return new Class<T>(intCallback(int32));
}
else if (index is ulong uint64)
{
if (uint64 > int.MaxValue) throw new ArgumentOutOfRangeException("index", IndexGraterMax);
var int32 = Convert.ToInt32(uint64);
return new Class<T>(intCallback(int32));
}
}
}
throw new InvalidOperationException(InvalidIndex);
}
/// <summary></summary>
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
var box = DynamicIndex(indexes, name =>
{
var property = GetProperty(name);
return ParseJToken(property?._jtoken);
}, index =>
{
return GetItem(index);
});
if (box == null)
{
result = null;
return false;
}
else
{
result = box.Value;
return true;
}
}
/// <summary></summary>
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
var box = DynamicIndex(indexes, name =>
{
_jobject[name] = ToJToken(value);
return true;
}, index =>
{
SetItem(index, value);
return true;
});
return box != null;
}
/// <summary></summary>
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
{
switch (binder.CallInfo.ArgumentNames.First().Lower())
{
case "tostring":
if (args == null)
{
result = ToString();
return true;
}
else
{
switch (args.Length)
{
case 0:
result = ToString();
return true;
case 1:
if (args[0] == null)
{
result = null;
return false;
}
else
{
if (args[0] is bool indented)
{
result = ToString(indented);
return true;
}
else if (args[0] is byte int8)
{
result = ToString(int8 == 1);
return true;
}
else if (args[0] is short int16)
{
result = ToString(int16 == 1);
return true;
}
else if (args[0] is int int32)
{
result = ToString(int32 == 1);
return true;
}
else if (args[0] is long int64)
{
result = ToString(int64 == 1);
return true;
}
else
{
result = null;
return false;
}
}
default:
result = null;
return false;
}
}
default:
result = null;
return false;
}
}
/// <summary></summary>
public override bool TryConvert(ConvertBinder binder, out object result)
{
if (binder.Type.Equals(typeof(Json)))
{
result = this;
return true;
}
if (binder.Type.Equals(typeof(string)))
{
result = ToString();
return true;
}
result = null;
return false; return false;
} }
@ -2247,29 +2493,40 @@ namespace Apewer
#region DateTime #region DateTime
static Func<DateTime, string> _datetime_serializer = null;
static Func<object, DateTime> _datetime_deserializer = null;
/// <summary>自定义 DateTime 序列化程序。</summary> /// <summary>自定义 DateTime 序列化程序。</summary>
public static Func<DateTime, string> DateTimeSerializer { get; set; } public static Func<DateTime, string> DateTimeSerializer { get => _datetime_serializer; set => _datetime_serializer = value; }
/// <summary>自定义 DateTime 反序列化程序。</summary> /// <summary>自定义 DateTime 反序列化程序。</summary>
public static Func<string, DateTime> DateTimeDeserializer { get; set; } public static Func<object, DateTime> DateTimeDeserializer { get => _datetime_deserializer; set => _datetime_deserializer = value; }
/// <summary>序列化 DateTime 实例。</summary> /// <summary>序列化 DateTime 实例。</summary>
public static string SerializeDateTime(DateTime dateTime) public static string SerializeDateTime(DateTime dateTime)
{ {
var serializer = DateTimeSerializer; var serializer = _datetime_serializer;
if (serializer != null) return serializer.Invoke(dateTime); if (serializer != null) return serializer.Invoke(dateTime);
return ClockUtility.Lucid(dateTime); return ClockUtility.Lucid(dateTime);
} }
/// <summary>序列化 DateTime 实例。</summary> /// <summary>序列化 DateTime 实例。</summary>
public static DateTime DeserializeDateTime(string text) public static DateTime DeserializeDateTime(object value)
{ {
var deserializer = DateTimeDeserializer; var deserializer = _datetime_deserializer;
if (deserializer != null) return deserializer.Invoke(text); if (deserializer != null) return deserializer.Invoke(value);
try
{
if (value is DateTime dt) return dt;
var ndt = ClockUtility.Parse(text); var text = value.ToString();
return ndt == null ? default(DateTime) : ndt.Value; var parse = ClockUtility.Parse(text);
if (parse != null) return parse.Value;
}
catch { }
return default;
} }
#endregion #endregion

2
Apewer/Network/Extension.cs

@ -134,7 +134,7 @@ namespace Apewer.Network
{ {
if (reply == null) return null; if (reply == null) return null;
var buffer = reply.Buffer.X2(); var buffer = BytesUtility.ToX2(reply.Buffer);
if (buffer.Replace("0", "").IsEmpty()) buffer = null; if (buffer.Replace("0", "").IsEmpty()) buffer = null;
var json = Json.NewObject(); var json = Json.NewObject();

310
Apewer/NumberUtility.cs

@ -187,17 +187,254 @@ namespace Apewer
#endregion #endregion
#region 平均值
/// <summary>平均值。</summary>
public static float Average(params float[] values)
{
if (values == null) return 0F;
var amount = 0F;
foreach (var i in values) amount += i;
return amount / values.Length;
}
/// <summary>平均值。</summary> /// <summary>平均值。</summary>
public static double Average(params double[] values) public static double Average(params double[] values)
{ {
var amount = 0d; if (values == null) return 0D;
var amount = 0D;
foreach (var i in values) amount += i; foreach (var i in values) amount += i;
return amount / Convert.ToDouble(values.Length); return amount / Convert.ToDouble(values.Length);
} }
#region 最小值、最大值。 /// <summary>平均值。</summary>
public static decimal Average(params decimal[] values)
{
if (values == null) return 0M;
var amount = 0M;
foreach (var i in values) amount += i;
return amount / Convert.ToDecimal(values.Length);
}
#endregion
#region 标准差
/// <summary>计算标准差。</summary>
/// <param name="values">值。</param>
/// <param name="sample">计算样本标准差,分母为 n - 1。此值为 false 时计算总体标准差。</param>
public static double Stdev(this IEnumerable<double> values, bool sample = false)
{
if (values == null) return 0;
var count = 0;
var sum = 0D;
var up = 0D;
if (values is double[] array)
{
count = array.Length;
if (count < 1) return 0;
for (var i = 0; i < count; i++) sum += array[i];
var avg = sum / count;
for (var i = 0; i < count; i++) up += (array[i] - avg) * (array[i] - avg);
}
else if (values is IList<double> list)
{
count = list.Count;
if (count < 1) return 0;
for (var i = 0; i < count; i++) sum += list[i];
var avg = sum / count;
for (var i = 0; i < count; i++) up += (list[i] - avg) * (list[i] - avg);
}
else
{
foreach (var value in values)
{
sum += value;
count++;
}
if (count < 1) return 0;
var avg = sum / count;
foreach (var value in values)
{
up += (value - avg) * (value - avg);
count++;
}
}
// 样本标准差,最少要 2 个值。
if (sample)
{
var down = count - 1;
if (down == 0) return 0;
var sigma = Math.Sqrt(up / down);
return sigma;
}
else
{
var sigma = Math.Sqrt(up / count);
return sigma;
}
}
/// <summary>计算总体标准差,分母为 n。</summary>
/// <param name="values">值。</param>
public static double StdevP(this IEnumerable<double> values) => Stdev(values, false);
/// <summary>计算样本标准差,分母为 n - 1。</summary>
/// <param name="values">值。</param>
public static double StdevS(this IEnumerable<double> values) => Stdev(values, true);
/// <summary>计算标准差。</summary>
/// <param name="values">值。</param>
/// <param name="sample">计算样本标准差,分母为 n - 1。此值为 false 时计算总体标准差。</param>
public static float Stdev(this IEnumerable<float> values, bool sample = false)
{
if (values == null) return 0;
var count = 0;
var sum = 0F;
var up = 0F;
if (values is float[] array)
{
count = array.Length;
if (count < 1) return 0;
for (var i = 0; i < count; i++) sum += array[i];
var avg = sum / count;
for (var i = 0; i < count; i++) up += (array[i] - avg) * (array[i] - avg);
}
else if (values is IList<float> list)
{
count = list.Count;
if (count < 1) return 0;
for (var i = 0; i < count; i++) sum += list[i];
var avg = sum / count;
for (var i = 0; i < count; i++) up += (list[i] - avg) * (list[i] - avg);
}
else
{
foreach (var value in values)
{
sum += value;
count++;
}
if (count < 1) return 0;
var avg = sum / count;
foreach (var value in values)
{
up += (value - avg) * (value - avg);
count++;
}
}
// 样本标准差,最少要 2 个值。
if (sample)
{
var down = count - 1;
if (down == 0) return 0;
var sigma = Convert.ToSingle(Math.Sqrt(up / down));
return sigma;
}
else
{
var sigma = Convert.ToSingle(Math.Sqrt(up / count));
return sigma;
}
}
/// <summary>计算总体标准差,分母为 n。</summary>
/// <param name="values">值。</param>
public static float StdevP(this IEnumerable<float> values) => Stdev(values, false);
/// <summary>计算样本标准差,分母为 n - 1。</summary>
/// <param name="values">值。</param>
public static float StdevS(this IEnumerable<float> values) => Stdev(values, true);
/// <summary>计算标准差。</summary>
/// <param name="values">值。</param>
/// <param name="sample">计算样本标准差,分母为 n - 1。此值为 false 时计算总体标准差。</param>
public static decimal Stdev(this IEnumerable<decimal> values, bool sample = false)
{
if (values == null) return 0;
var count = 0;
var sum = 0M;
var up = 0M;
if (values is decimal[] array)
{
count = array.Length;
if (count < 1) return 0;
for (var i = 0; i < count; i++) sum += array[i];
var avg = sum / count;
for (var i = 0; i < count; i++) up += (array[i] - avg) * (array[i] - avg);
}
else if (values is IList<decimal> list)
{
count = list.Count;
if (count < 1) return 0;
for (var i = 0; i < count; i++) sum += list[i];
var avg = sum / count;
for (var i = 0; i < count; i++) up += (list[i] - avg) * (list[i] - avg);
}
else
{
foreach (var value in values)
{
sum += value;
count++;
}
if (count < 1) return 0;
var avg = sum / count;
foreach (var value in values)
{
up += (value - avg) * (value - avg);
count++;
}
}
// 样本标准差,最少要 2 个值。
if (sample)
{
var down = count - 1;
if (down == 0) return 0;
var sigma = Convert.ToDecimal(Math.Sqrt(Convert.ToDouble(up / down)));
return sigma;
}
else
{
var sigma = Convert.ToDecimal(Math.Sqrt(Convert.ToDouble(up / count)));
return sigma;
}
}
/// <summary>计算总体标准差,分母为 n。</summary>
/// <param name="values">值。</param>
public static decimal StdevP(this IEnumerable<decimal> values) => Stdev(values, false);
/// <summary>计算样本标准差,分母为 n - 1。</summary>
/// <param name="values">值。</param>
public static decimal StdevS(this IEnumerable<decimal> values) => Stdev(values, true);
#endregion
#region 最小值、最大值、极差
private static T GetMost<T>(Func<T, T, bool> replace, T[] values) private static T Most<T>(Func<T, T, bool> replace, T[] values)
{ {
if (values == null) return default; if (values == null) return default;
@ -223,69 +460,56 @@ namespace Apewer
return most; return most;
} }
// 无法输出正确结果。
// public static T GetMin<T>(params IComparable[] values)
// {
// var result = GetMost((a, b) => a.CompareTo(b) == 1, values);
// return result == null ? default : (T)result;
// }
// 无法输出正确结果。
// public static T GetMin<T>(params IComparable<T>[] values)
// {
// var result = GetMost((a, b) => a.CompareTo((T)b) == 1, values);
// return result == null ? default : (T)result;
// }
// 无法输出正确结果。
// public static T GetMax<T>(params IComparable[] values)
// {
// var result = GetMost((a, b) => a.CompareTo(b) == -1, values);
// return result == null ? default : (T)result;
// }
// 无法输出正确结果。
// public static T GetMax<T>(params IComparable<T>[] values)
// {
// var result = GetMost((a, b) => a.CompareTo((T)b) == -1, values);
// return result == null ? default : (T)result;
// }
/// <summary>最小值。</summary> /// <summary>最小值。</summary>
public static byte Min(params byte[] values) => GetMost((a, b) => a > b, values); public static byte Min(params byte[] values) => Most((a, b) => a > b, values);
/// <summary>最小值。</summary> /// <summary>最小值。</summary>
public static int Min(params int[] values) => GetMost((a, b) => a > b, values); public static int Min(params int[] values) => Most((a, b) => a > b, values);
/// <summary>最小值。</summary> /// <summary>最小值。</summary>
public static long Min(params long[] values) => GetMost((a, b) => a > b, values); public static long Min(params long[] values) => Most((a, b) => a > b, values);
/// <summary>最小值。</summary> /// <summary>最小值。</summary>
public static float Min(params float[] values) => GetMost((a, b) => a > b, values); public static float Min(params float[] values) => Most((a, b) => a > b, values);
/// <summary>最小值。</summary> /// <summary>最小值。</summary>
public static double Min(params double[] values) => GetMost((a, b) => a > b, values); public static double Min(params double[] values) => Most((a, b) => a > b, values);
/// <summary>最小值。</summary> /// <summary>最小值。</summary>
public static decimal Min(params decimal[] values) => GetMost((a, b) => a > b, values); public static decimal Min(params decimal[] values) => Most((a, b) => a > b, values);
/// <summary>最大值。</summary> /// <summary>最大值。</summary>
public static byte Max(params byte[] values) => GetMost((a, b) => a < b, values); public static byte Max(params byte[] values) => Most((a, b) => a < b, values);
/// <summary>最大值。</summary> /// <summary>最大值。</summary>
public static int Max(params int[] values) => GetMost((a, b) => a < b, values); public static int Max(params int[] values) => Most((a, b) => a < b, values);
/// <summary>最大值。</summary> /// <summary>最大值。</summary>
public static long Max(params long[] values) => GetMost((a, b) => a < b, values); public static long Max(params long[] values) => Most((a, b) => a < b, values);
/// <summary>最大值。</summary> /// <summary>最大值。</summary>
public static float Max(params float[] values) => GetMost((a, b) => a < b, values); public static float Max(params float[] values) => Most((a, b) => a < b, values);
/// <summary>最大值。</summary> /// <summary>最大值。</summary>
public static double Max(params double[] values) => GetMost((a, b) => a < b, values); public static double Max(params double[] values) => Most((a, b) => a < b, values);
/// <summary>最大值。</summary> /// <summary>最大值。</summary>
public static decimal Max(params decimal[] values) => GetMost((a, b) => a < b, values); public static decimal Max(params decimal[] values) => Most((a, b) => a < b, values);
/// <summary>极差。</summary>
public static int Range(params int[] values) => Max(values) - Min(values);
/// <summary>极差。</summary>
public static long Range(params long[] values) => Max(values) - Min(values);
/// <summary>极差。</summary>
public static float Range(params float[] values) => Max(values) - Min(values);
/// <summary>极差。</summary>
public static double Range(params double[] values) => Max(values) - Min(values);
/// <summary>极差。</summary>
public static decimal Range(params decimal[] values) => Max(values) - Min(values);
#endregion #endregion

71
Apewer/RuntimeUtility.cs

@ -14,7 +14,7 @@ namespace Apewer
{ {
/// <summary>运行时实用工具。</summary> /// <summary>运行时实用工具。</summary>
public class RuntimeUtility public static class RuntimeUtility
{ {
/// <summary>使用默认比较器判断相等。</summary> /// <summary>使用默认比较器判断相等。</summary>
@ -514,6 +514,47 @@ namespace Apewer
throw new ArgumentException($"参数 {arrayType} 不是有效的数组类型。"); throw new ArgumentException($"参数 {arrayType} 不是有效的数组类型。");
} }
/// <summary>是 Nullable&lt;T&gt; 类型。</summary>
public static bool IsNullableType(this Type type)
{
if (type.IsGenericType && !type.IsGenericTypeDefinition)
{
Type genericTypeDefinition = type.GetGenericTypeDefinition();
if ((object)genericTypeDefinition == typeof(Nullable<>))
{
return true;
}
}
return false;
}
/// <summary>是 Nullable&lt;T&gt; 类型。</summary>
public static bool IsNullableType(this Type type, out Type genericType)
{
if (type.IsGenericType && !type.IsGenericTypeDefinition)
{
Type genericTypeDefinition = type.GetGenericTypeDefinition();
if ((object)genericTypeDefinition == typeof(Nullable<>))
{
genericType = type.GetGenericArguments()[0];
return true;
}
}
genericType = null;
return false;
}
/// <summary>获取 Nullable&lt;T&gt; 实例的值。</summary>
public static object GetNullableValue(object nullable)
{
var type = typeof(Nullable<>);
var property = type.GetProperty("Value");
var getter = property.GetGetMethod();
var value = getter.Invoke(nullable, null);
return value;
}
#endregion #endregion
#region Collect & Dispose #region Collect & Dispose
@ -631,6 +672,34 @@ namespace Apewer
return new Timer((x) => Invoke((Action)x, @try), action, delay > 0 ? delay : 0, -1); return new Timer((x) => Invoke((Action)x, @try), action, delay > 0 ? delay : 0, -1);
} }
/// <summary>尝试在主线程调用。</summary>
/// <exception cref="ArgumentNullException" />
/// <exception cref="InvalidOperationException" />
public static void OnMainThread(Action action)
{
if (action == null) throw new ArgumentNullException(nameof(action));
var context = SynchronizationContext.Current;
if (context == null) throw new InvalidOperationException("未获取到当前线程的同步上下文。");
context.Send(state => { action.Invoke(); }, null);
}
/// <summary>尝试在主线程调用。</summary>
/// <exception cref="ArgumentNullException" />
/// <exception cref="InvalidOperationException" />
public static TResult OnMainThread<TResult>(Func<TResult> func)
{
if (func == null) throw new ArgumentNullException(nameof(func));
var context = SynchronizationContext.Current;
if (context == null) throw new InvalidOperationException("未获取到当前线程的同步上下文。");
var result = default(TResult);
context.Send(state => result = func.Invoke(), null);
return result;
}
#endregion #endregion
#region Assembly #region Assembly

51
Apewer/Source/SourceUtility.cs

@ -599,32 +599,59 @@ namespace Apewer.Source
/// <summary>启动事务,执行指定的过程并在完成后提交事务。若过程被异常打断,则回滚事务。</summary> /// <summary>启动事务,执行指定的过程并在完成后提交事务。若过程被异常打断,则回滚事务。</summary>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
/// <exception cref="SqlException"></exception> /// <exception cref="SqlException"></exception>
public static void InTransaction<T>(this IDbAdo source, Action action) public static void InTransaction(this IDbAdo source, Action action)
{ {
// 检查参数。 // 检查参数。
if (source == null) throw new ArgumentNullException(nameof(source), "数据源无效。"); if (source == null) throw new ArgumentNullException(nameof(source), "数据源无效。");
if (action == null) throw new ArgumentNullException(nameof(action), "没有指定要执行的过程。"); if (action == null) throw new ArgumentNullException(nameof(action), "没有指定要在事物中执行的程序。");
InTransaction<object>(source, () =>
{
action.Invoke();
return null;
});
}
/// <summary>启动事务,执行指定的过程并在完成后提交事务。若过程被异常打断,则回滚事务。</summary>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="SqlException"></exception>
public static T InTransaction<T>(this IDbAdo source, Func<T> func)
{
// 检查参数。
if (source == null) throw new ArgumentNullException(nameof(source), "数据源无效。");
if (func == null) throw new ArgumentNullException(nameof(func), "没有指定要在事物中执行的程序。");
// 已经存在事务。
if (source.Transaction != null) return func.Invoke();
// 启动事务。 // 启动事务。
var begin = source.Begin(); var begin = source.Begin();
if (begin.NotEmpty()) throw new SqlException("无法启动事务:" + begin); if (begin.NotEmpty()) throw new SqlException("无法启动事务:" + begin);
var result = default(T);
var success = false;
try try
{ {
// 在事务内运行。 // 在事务内运行。
action.Invoke(); result = func.Invoke();
success = true;
// 执行成功,提交事务。
var commit = source.Commit();
if (!string.IsNullOrEmpty(commit)) throw new SqlException(commit);
} }
catch (Exception ex) finally
{ {
// 执行失败,回滚事务。 if (success)
try { source.Rollback(); } catch { } {
// 执行成功,提交事务。
throw ex; var commit = source.Commit();
if (!string.IsNullOrEmpty(commit)) throw new SqlException(commit);
}
else
{
// 执行失败,回滚事务。
try { source.Rollback(); } catch { }
}
} }
return result;
} }
#endregion #endregion

30
Apewer/Web/ApiUtility.cs

@ -824,36 +824,6 @@ namespace Apewer.Web
#endregion #endregion
#region Application & Path
/// <summary>MapPath。</summary>
public static string MapPath(params string[] names)
{
var list = new List<string>();
if (names != null)
{
var invalid = StorageUtility.InvalidPathChars;
foreach (var name in names)
{
if (string.IsNullOrEmpty(name)) continue;
var split = name.Split('/', '\\');
foreach (var s in split)
{
var sb = new StringBuilder();
foreach (var c in s)
{
if (Array.IndexOf(invalid, "c") < 0) sb.Append(c);
}
var t = sb.ToString();
if (!string.IsNullOrEmpty(t)) list.Add(t);
}
}
}
return list.Count > 1 ? StorageUtility.CombinePath(list.ToArray()) : "";
}
#endregion
#region ApiFunction Parameters #region ApiFunction Parameters

26
Apewer/_Extensions.cs

@ -192,7 +192,7 @@ public static class Extensions
public static string AntiInject(this string @this, int length = -1, string blacklist = null) => TextUtility.AntiInject(@this, length, blacklist); public static string AntiInject(this string @this, int length = -1, string blacklist = null) => TextUtility.AntiInject(@this, length, blacklist);
/// <summary>将 Base64 字符串转换为字节数组。</summary> /// <summary>将 Base64 字符串转换为字节数组。</summary>
public static byte[] Base64(this string @this) => BytesUtility.FromBase64(@this); public static byte[] Base64(this string text) => BytesUtility.FromBase64(text);
/// <summary>对 URL 编码。</summary> /// <summary>对 URL 编码。</summary>
public static string EncodeUrl(this string @this) => UrlEncoding.Encode(@this); public static string EncodeUrl(this string @this) => UrlEncoding.Encode(@this);
@ -223,36 +223,12 @@ public static class Extensions
#region Byte[] #region Byte[]
/// <summary>将字节数组格式化为十六进制字符串,默认为大写。<para>例:D41D8CD98F00B204E9800998ECF8427E</para></summary>
public static string X2(this byte[] @this, bool upperCase = true) => BytesUtility.ToX2(upperCase, @this);
/// <summary>克隆字节数组。当源为 NULL 时,将获取零元素字节数组。</summary>
public static byte[] Clone(this byte[] @this) => BytesUtility.Clone(@this);
/// <summary>确定此字节数组实例的开头是否与指定的字节数组匹配。</summary>
public static bool Starts(this byte[] @this, params byte[] head) => BytesUtility.StartsWith(@this, head);
/// <summary>确定此字节数组实例的结尾是否与指定的字节数组匹配。</summary>
public static bool Ends(this byte[] @this, params byte[] head) => BytesUtility.EndsWith(@this, head);
/// <summary>将字节数组转换为 Base64 字符串。</summary> /// <summary>将字节数组转换为 Base64 字符串。</summary>
public static string Base64(this byte[] @this) => BytesUtility.ToBase64(@this); public static string Base64(this byte[] @this) => BytesUtility.ToBase64(@this);
/// <summary>获取 MD5 值。</summary>
public static byte[] MD5(params byte[] @this) => BytesUtility.MD5(@this);
/// <summary>获取 SHA1 值。</summary>
public static byte[] SHA1(params byte[] @this) => BytesUtility.SHA1(@this);
/// <summary>获取 SHA256 值。</summary>
public static byte[] SHA256(params byte[] @this) => BytesUtility.SHA256(@this);
/// <summary>将字节数组转换为文本,默认为 UTF-8。</summary> /// <summary>将字节数组转换为文本,默认为 UTF-8。</summary>
public static string Text(this byte[] @this, Encoding encoding = null) => TextUtility.FromBytes(@this, encoding); public static string Text(this byte[] @this, Encoding encoding = null) => TextUtility.FromBytes(@this, encoding);
/// <summary>为字节数组增加字节。</summary>
public static byte[] Append(this byte[] @this, params byte[] bytes) => BytesUtility.Merge(@this, bytes);
/// <summary>检查字节数组是 UTF-8 文本,默认最多检测 1MB 数据(指定为 0 将不限制检查长度)。</summary> /// <summary>检查字节数组是 UTF-8 文本,默认最多检测 1MB 数据(指定为 0 将不限制检查长度)。</summary>
public static bool IsUTF8(this byte[] @this, int checkLength = 1048576) => TextUtility.IsUTF8(@this, checkLength, null); public static bool IsUTF8(this byte[] @this, int checkLength = 1048576) => TextUtility.IsUTF8(@this, checkLength, null);

13
ChangeLog.md

@ -1,6 +1,19 @@
 
### 最新提交 ### 最新提交
### 6.7.6
- 新特性
- CollectionUtility:增加数组的 Push 和 Unshift 方法;
- Json:支持序号索引访问 Json 数组;
- Json:静态属性 Json.DeserializeDateTime 可自定义 DateTime 的反序列化,允许用户代码转换字符串或 DateTime.Kind;
- NumberUtility:增加平均值、标准差、极差;
- Source:InTransaction 支持使用已存在的事务;
- Windows:增加扩展方法 AllowResizeForm,支持鼠标调整无边框窗体的大小。
- 问题修正
- ByetsUtility:修正 ToBase64 的 null 引用问题;
- Source:InTransaction(Action) 去除无用的泛型要求;
- Source:InTransaction 不再捕获异常,直接抛出 action 的异常。
### 6.7.5 ### 6.7.5
- 问题修正 - 问题修正
- Query:删除了一些访问单元格的重载方法,减少歧义; - Query:删除了一些访问单元格的重载方法,减少歧义;

Loading…
Cancel
Save