简介
粘土对象是我从 Furion 框架中扒出来的,是一种可以模拟弱语言特性的对象,类似 Javascript 一样操作对象。只需通过 Clay 类初始化即可。
为什么起名为 “粘土” 呢?因为这个对象可以自由的添加属性,移除属性,又可以转换成任何对象,具有可拓展、可塑造的特点。
粘土性能略输于强类型调用。Clay 对象是继承自 DynamicObject 的一个特殊对象,提供了向弱语言一样操作对象的方法及索引。
使用场景
粘土对象常用于需要动态构建对象的地方,如 CMS 系统的 ViewModel,或者运行时创建一个新的对象,或者请求第三方 API 情况。
如何使用
创建一个对象
// 创建一个空的粘土对象
dynamic clay = new Clay();
// 从现有的对象创建
var clay2 = Clay.Object(new {});
// 从 json 字符串创建,可用于第三方 API 对接,非常有用
var clay3 = Clay.Parse(@"{""foo"":""json"", ""bar"":100, ""nest"":{ ""foobar"":true } }");
29.3.2 读取/获取属性#
var clay = Clay.Object(new
{
Foo = "json",
Bar = 100,
Nest = new
{
Foobar = true
}
});
var r1 = clay.Foo; // "json" - string类型
var r2 = clay.Bar; // 100 - double类型
var r3 = clay.Nest.Foobar; // true - bool类型
var r4 = clay["Nest"]["Foobar"]; // 还可以和 Javascript 一样通过索引器获取
29.3.3 新增属性
var clay = Clay.Object(new
{
Foo = "json",
Bar = 100,
Nest = new
{
Foobar = true
}
});
// 新增
clay.Arr = new string[] { "NOR", "XOR" }; // 添加一个数组
clay.Obj1 = new City { }; // 新增一个实例对象
clay.Obj2 = new { Foo = "abc", Bar = 100 }; // 新增一个匿名类
更新属性值
var clay = Clay.Object(new
{
Foo = "json",
Bar = 100,
Nest = new
{
Foobar = true
}
});
// 更新
clay.Foo = "Furion";
clay["Nest"].Foobar = false;
clay.Nest["Foobar"] = true;
删除属性
var clay = Clay.Object(new
{
Foo = "json",
Bar = 100,
Nest = new
{
Foobar = true
},
Arr = new string[] { "NOR", "XOR" }
});
// 删除操作
clay.Delete("Foo"); // 通过 Delete 方法删除
clay.Arr.Delete(0); // 支持数组 Delete 索引删除
clay("Bar"); // 支持直接通过对象作为方法删除
clay.Arr(1); // 支持数组作为方法删除
判断属性是否存在
var clay = Clay.Object(new
{
Foo = "json",
Bar = 100,
Nest = new
{
Foobar = true
},
Arr = new string[] { "NOR", "XOR" }
});
// 判断属性是否存在
var a = clay.IsDefined("Foo"); // true
var b = clay.IsDefined("Foooo"); // false
var c = clay.Foo(); // true
var d = clay.Foooo(); // false;
遍历对象
var clay = Clay.Object(new
{
Foo = "json",
Bar = 100,
Nest = new
{
Foobar = true
},
Arr = new string[] { "NOR", "XOR" }
});
// 遍历数组
foreach (string item in clay.Arr)
{
Console.WriteLine(item); // NOR, XOR
}
// 遍历整个对象属性及值,类似 JavaScript 的 for (var p in obj)
foreach (KeyValuePair<string, dynamic> item in clay)
{
Console.WriteLine(item.Key + ":" + item.Value); // Foo:json, Bar: 100, Nest: { "Foobar":true}, Arr:["NOR","XOR"]
}
转换成具体对象
dynamic clay = new Clay();
clay.Arr = new string[] { "Furion", "Fur" };
// 数组转换示例
var a1 = clay.Arr.Deserialize<string[]>(); // 通过 Deserialize 方法
var a2 = (string[])clay.Arr; // 强制转换
string[] a3 = clay.Arr; // 声明方式
// 对象转换示例
clay.City = new City { Id = 1, Name = "中山市" };
var c1 = clay.City.Deserialize<City>(); // 通过 Deserialize 方法
var c2 = (City)clay.City; // 强制转换
City c3 = clay.City; // 声明方式
固化粘土
固化粘土在很多时候和序列化很像,但是如果直接调用 Deserialize 或 Deserialize 无法返回实际类型,所以就有了固化类型的功能,如:
// 返回 object
var obj = clay.Solidify();
// 返回 dynamic
var obj1 = clay.Solidify<dynamic>();
// 返回其他任意类型
var obj2 = clay.Solidify<City>();
29.3.10 输出 JSON#
var clay = Clay.Object(new
{
Foo = "json",
Bar = 100,
Nest = new
{
Foobar = true
},
Arr = new string[] { "NOR", "XOR" }
});
输出 JSON
var json = clay.ToString(); // "{\"Foo\":\"json\",\"Bar\":100,\"Nest\":{\"Foobar\":true},\"Arr\":[\"NOR\",\"XOR\"]}"
29.3.11 输出 XML 对象#
var clay = Clay.Object(new
{
Foo = "json",
Bar = 100,
Nest = new
{
Foobar = true
},
Arr = new string[] { "NOR", "XOR" }
});
关键字处理
dynamic clay = new Clay();
clay.@int = 1;
clay.@event = "事件";
源码
Clay
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
namespace ClayObject
{
/// <summary>
/// 粘土对象
/// </summary>
public class Clay : DynamicObject
{
/// <summary>
/// 构造函数
/// </summary>
public Clay()
{
XmlElement = new XElement("root", CreateTypeAttr(JsonType.@object));
jsonType = JsonType.@object;
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="element"></param>
/// <param name="type"></param>
private Clay(XElement element, JsonType type)
{
Debug.Assert(type == JsonType.array || type == JsonType.@object);
XmlElement = element;
jsonType = type;
}
/// <summary>
/// 是否是 Object 类型
/// </summary>
public bool IsObject => jsonType == JsonType.@object;
/// <summary>
/// 是否是 Array 类型
/// </summary>
public bool IsArray => jsonType == JsonType.array;
/// <summary>
/// XML 元素
/// </summary>
public XElement XmlElement { get; private set; }
/// <summary>
/// 创建一个超级类型
/// </summary>
/// <returns></returns>
public static dynamic Object()
{
return new Clay();
}
/// <summary>
/// 基于现有类型创建一个超级类型
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static dynamic Object(object obj)
{
return Parse(Serialize(obj));
}
/// <summary>
/// 将 Json 转换成动态类型
/// </summary>
/// <param name="json"></param>
/// <returns></returns>
public static dynamic Parse(string json)
{
return Parse(json, Encoding.Unicode);
}
/// <summary>
/// 将 Json 转换成动态类型
/// </summary>
/// <param name="json"></param>
/// <param name="encoding"></param>
/// <returns></returns>
public static dynamic Parse(string json, Encoding encoding)
{
using var reader = JsonReaderWriterFactory.CreateJsonReader(encoding.GetBytes(json), XmlDictionaryReaderQuotas.Max);
return ToValue(XElement.Load(reader));
}
/// <summary>
/// 将 Steam 转换成动态类型
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
public static dynamic Parse(Stream stream)
{
using var reader = JsonReaderWriterFactory.CreateJsonReader(stream, XmlDictionaryReaderQuotas.Max);
return ToValue(XElement.Load(reader));
}
/// <summary>
/// 将 Steam 转换成动态类型
/// </summary>
/// <param name="stream"></param>
/// <param name="encoding"></param>
/// <returns></returns>
public static dynamic Parse(Stream stream, Encoding encoding)
{
using var reader = JsonReaderWriterFactory.CreateJsonReader(stream, encoding, XmlDictionaryReaderQuotas.Max, _ => { });
return ToValue(XElement.Load(reader));
}
/// <summary>
/// 序列化对象
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string Serialize(object obj)
{
return CreateJsonString(new XStreamingElement("root", CreateTypeAttr(GetJsonType(obj)), CreateJsonNode(obj)));
}
/// <summary>
/// 是否定义某个键
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public bool IsDefined(string name)
{
return IsObject && (XmlElement.Element(name) != null);
}
/// <summary>
/// 判断数组索引是否存在
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public bool IsDefined(int index)
{
return IsArray && (XmlElement.Elements().ElementAtOrDefault(index) != null);
}
/// <summary>
/// 删除键
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public bool Delete(string name)
{
var elem = XmlElement.Element(name);
if (elem != null)
{
elem.Remove();
return true;
}
else return false;
}
/// <summary>
/// 根据索引删除元素
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public bool Delete(int index)
{
var elem = XmlElement.Elements().ElementAtOrDefault(index);
if (elem != null)
{
elem.Remove();
return true;
}
else return false;
}
/// <summary>
/// 反序列化
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T Deserialize<T>()
{
return (T)Deserialize(typeof(T));
}
/// <summary>
/// 删除
/// </summary>
/// <param name="binder"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
{
result = (IsArray)
? Delete((int)args[0])
: Delete((string)args[0]);
return true;
}
/// <summary>
/// 判断是否定义
/// </summary>
/// <param name="binder"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (args.Length > 0)
{
result = null;
return false;
}
result = IsDefined(binder.Name);
return true;
}
/// <summary>
/// 支持 Foreach 遍历
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryConvert(ConvertBinder binder, out object result)
{
if (binder.Type == typeof(IEnumerable) || binder.Type == typeof(object[]))
{
var ie = (IsArray)
? XmlElement.Elements().Select(x => ToValue(x))
: XmlElement.Elements().Select(x => (dynamic)new KeyValuePair<string, object>(x.Name.LocalName, ToValue(x)));
result = (binder.Type == typeof(object[])) ? ie.ToArray() : ie;
}
else
{
result = Deserialize(binder.Type);
}
return true;
}
/// <summary>
/// 获取索引值
/// </summary>
/// <param name="binder"></param>
/// <param name="indexes"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
return (IsArray)
? TryGet(XmlElement.Elements().ElementAtOrDefault((int)indexes[0]), out result)
: TryGet(XmlElement.Element((string)indexes[0]), out result);
}
/// <summary>
/// 获取成员值
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return (IsArray)
? TryGet(XmlElement.Elements().ElementAtOrDefault(int.Parse(binder.Name)), out result)
: TryGet(XmlElement.Element(binder.Name), out result);
}
/// <summary>
/// 设置索引
/// </summary>
/// <param name="binder"></param>
/// <param name="indexes"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
return (IsArray)
? TrySet((int)indexes[0], value)
: TrySet((string)indexes[0], value);
}
/// <summary>
/// 设置成员
/// </summary>
/// <param name="binder"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
return (IsArray)
? TrySet(int.Parse(binder.Name), value)
: TrySet(binder.Name, value);
}
/// <summary>
/// 获取动态成员名称
/// </summary>
/// <returns></returns>
public override IEnumerable<string> GetDynamicMemberNames()
{
return (IsArray)
? XmlElement.Elements().Select((x, i) => i.ToString())
: XmlElement.Elements().Select(x => x.Name.LocalName);
}
/// <summary>
/// 重写 .ToString()
/// </summary>
/// <returns></returns>
public override string ToString()
{
// <foo type="null"></foo> is can't serialize. replace to <foo type="null" />
foreach (var elem in XmlElement.Descendants().Where(x => x.Attribute("type").Value == "null"))
{
elem.RemoveNodes();
}
return CreateJsonString(new XStreamingElement("root", CreateTypeAttr(jsonType), XmlElement.Elements()));
}
/// <summary>
/// 固化粘土,也就是直接输出对象
/// </summary>
/// <returns></returns>
public object Solidify()
{
return Solidify<object>();
}
/// <summary>
/// 固化粘土,也就是直接输出对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T Solidify<T>()
{
return System.Text.Json.JsonSerializer.Deserialize<T>(ToString());
//return JSON.Deserialize<T>(ToString());
}
/// <summary>
/// JSON 类型
/// </summary>
private enum JsonType
{
@string, number, boolean, @object, array, @null
}
/// <summary>
/// XElement 转动态类型
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
private static dynamic ToValue(XElement element)
{
var type = (JsonType)Enum.Parse(typeof(JsonType), element.Attribute("type").Value);
return type switch
{
JsonType.boolean => (bool)element,
JsonType.number => (double)element,
JsonType.@string => (string)element,
JsonType.@object or JsonType.array => new Clay(element, type),
_ => null,
};
}
/// <summary>
/// 获取 JSON 类型
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
private static JsonType GetJsonType(object obj)
{
if (obj == null) return JsonType.@null;
return Type.GetTypeCode(obj.GetType()) switch
{
TypeCode.Boolean => JsonType.boolean,
TypeCode.String or TypeCode.Char or TypeCode.DateTime => JsonType.@string,
TypeCode.Int16 or TypeCode.Int32 or TypeCode.Int64 or TypeCode.UInt16 or TypeCode.UInt32 or TypeCode.UInt64 or TypeCode.Single or TypeCode.Double or TypeCode.Decimal or TypeCode.SByte or TypeCode.Byte => JsonType.number,
TypeCode.Object => (obj is IEnumerable) ? JsonType.array : JsonType.@object,
_ => JsonType.@null,
};
}
/// <summary>
/// 创建类型属性
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static XAttribute CreateTypeAttr(JsonType type)
{
return new XAttribute("type", type.ToString());
}
/// <summary>
/// 创建 JSON 节点
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
private static object CreateJsonNode(object obj)
{
var type = GetJsonType(obj);
return type switch
{
JsonType.@string or JsonType.number => obj,
JsonType.boolean => obj.ToString().ToLower(),
JsonType.@object => CreateXObject(obj),
JsonType.array => CreateXArray(obj as IEnumerable),
_ => null,
};
}
/// <summary>
/// 创建 XStreamingElement 对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
private static IEnumerable<XStreamingElement> CreateXArray<T>(T obj) where T : IEnumerable
{
return obj.Cast<object>()
.Select(o => new XStreamingElement("item", CreateTypeAttr(GetJsonType(o)), CreateJsonNode(o)));
}
/// <summary>
/// 创建 XStreamingElement 对象
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
private static IEnumerable<XStreamingElement> CreateXObject(object obj)
{
return obj.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Select(pi => new { pi.Name, Value = pi.GetValue(obj, null) })
.Select(a => new XStreamingElement(a.Name, CreateTypeAttr(GetJsonType(a.Value)), CreateJsonNode(a.Value)));
}
/// <summary>
/// 创建 JSON 字符串
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
private static string CreateJsonString(XStreamingElement element)
{
using var ms = new MemoryStream();
using var writer = JsonReaderWriterFactory.CreateJsonWriter(ms, Encoding.Unicode);
element.WriteTo(writer);
writer.Flush();
return Encoding.Unicode.GetString(ms.ToArray());
}
/// <summary>
/// JSON 类型
/// </summary>
private readonly JsonType jsonType;
/// <summary>
/// 读取值
/// </summary>
/// <param name="element"></param>
/// <param name="result"></param>
/// <returns></returns>
private static bool TryGet(XElement element, out object result)
{
if (element == null)
{
result = null;
return false;
}
result = ToValue(element);
return true;
}
/// <summary>
/// 设置值
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <returns></returns>
private bool TrySet(string name, object value)
{
var type = GetJsonType(value);
var element = XmlElement.Element(name);
if (element == null)
{
XmlElement.Add(new XElement(name, CreateTypeAttr(type), CreateJsonNode(value)));
}
else
{
element.Attribute("type").Value = type.ToString();
element.ReplaceNodes(CreateJsonNode(value));
}
return true;
}
/// <summary>
/// 设置值
/// </summary>
/// <param name="index"></param>
/// <param name="value"></param>
/// <returns></returns>
private bool TrySet(int index, object value)
{
var type = GetJsonType(value);
var e = XmlElement.Elements().ElementAtOrDefault(index);
if (e == null)
{
XmlElement.Add(new XElement("item", CreateTypeAttr(type), CreateJsonNode(value)));
}
else
{
e.Attribute("type").Value = type.ToString();
e.ReplaceNodes(CreateJsonNode(value));
}
return true;
}
/// <summary>
/// 反序列化
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private object Deserialize(Type type)
{
return (IsArray) ? DeserializeArray(type) : DeserializeObject(type);
}
/// <summary>
/// 反序列化值
/// </summary>
/// <param name="element"></param>
/// <param name="elementType"></param>
/// <returns></returns>
private static dynamic DeserializeValue(XElement element, Type elementType)
{
var value = ToValue(element);
if (value is Clay json)
{
value = json.Deserialize(elementType);
}
return ObjectExtensions.ChangeType(value, elementType);
}
/// <summary>
/// 反序列化对象
/// </summary>
/// <param name="targetType"></param>
/// <returns></returns>
private object DeserializeObject(Type targetType)
{
var result = Activator.CreateInstance(targetType);
var dict = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanWrite)
.ToDictionary(pi => pi.Name, pi => pi);
foreach (var item in XmlElement.Elements())
{
if (!dict.TryGetValue(item.Name.LocalName, out var propertyInfo)) continue;
var value = Clay.DeserializeValue(item, propertyInfo.PropertyType);
propertyInfo.SetValue(result, value, null);
}
return result;
}
/// <summary>
/// 序列化数组
/// </summary>
/// <param name="targetType"></param>
/// <returns></returns>
private object DeserializeArray(Type targetType)
{
if (targetType.IsArray)
{
var elemType = targetType.GetElementType();
dynamic array = Array.CreateInstance(elemType, XmlElement.Elements().Count());
var index = 0;
foreach (var item in XmlElement.Elements())
{
array[index++] = Clay.DeserializeValue(item, elemType);
}
return array;
}
else
{
var elemType = targetType.GetGenericArguments()[0];
dynamic list = Activator.CreateInstance(targetType);
foreach (var item in XmlElement.Elements())
{
list.Add(Clay.DeserializeValue(item, elemType));
}
return list;
}
}
}
}
DictionaryExtensions
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace ClayObject.Extensions
{
/// <summary>
/// 字典类型拓展类
/// </summary>
public static class DictionaryExtensions
{
/// <summary>
/// 将对象转成字典
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static IDictionary<string, object> ToDictionary(this object input)
{
if (input == null)
throw new ArgumentNullException(nameof(input));
if (input is IDictionary<string, object> dictionary)
return dictionary;
var properties = input.GetType().GetProperties();
var fields = input.GetType().GetFields();
var members = properties.Cast<MemberInfo>().Concat(fields.Cast<MemberInfo>());
return members.ToDictionary(m => m.Name, m => GetValue(input, m));
}
/// <summary>
/// 将对象转字典类型,其中值返回原始类型 Type 类型
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static IDictionary<string, Tuple<Type, object>> ToDictionaryWithType(this object input)
{
if (input == null)
throw new ArgumentNullException(nameof(input));
if (input is IDictionary<string, object> dictionary)
return dictionary.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value == null ?
new Tuple<Type, object>(typeof(object), kvp.Value) :
new Tuple<Type, object>(kvp.Value.GetType(), kvp.Value)
);
var dict = new Dictionary<string, Tuple<Type, object>>();
// 获取所有属性列表
foreach (var property in input.GetType().GetProperties())
{
dict.Add(property.Name, new Tuple<Type, object>(property.PropertyType, property.GetValue(input, null)));
}
// 获取所有成员列表
foreach (var field in input.GetType().GetFields())
{
dict.Add(field.Name, new Tuple<Type, object>(field.FieldType, field.GetValue(input)));
}
return dict;
}
/// <summary>
/// 获取成员值
/// </summary>
/// <param name="obj"></param>
/// <param name="member"></param>
/// <returns></returns>
private static object GetValue(object obj, MemberInfo member)
{
if (member is PropertyInfo info)
return info.GetValue(obj, null);
if (member is FieldInfo info1)
return info1.GetValue(obj);
throw new ArgumentException("Passed member is neither a PropertyInfo nor a FieldInfo.");
}
}
}
ExpandoObjectExtensions
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
namespace ClayObject.Extensions
{
/// <summary>
/// ExpandoObject 对象拓展
/// </summary>
public static class ExpandoObjectExtensions
{
/// <summary>
/// 将对象转 ExpandoObject 类型
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static ExpandoObject ToExpandoObject(this object value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
if (value is not ExpandoObject expando)
{
expando = new ExpandoObject();
var dict = (IDictionary<string, object>)expando;
var dictionary = value.ToDictionary();
foreach (var kvp in dictionary)
{
dict.Add(kvp);
}
}
return expando;
}
/// <summary>
/// 移除 ExpandoObject 对象属性
/// </summary>
/// <param name="expandoObject"></param>
/// <param name="propertyName"></param>
public static void RemoveProperty(this ExpandoObject expandoObject, string propertyName)
{
if (expandoObject == null)
throw new ArgumentNullException(nameof(expandoObject));
if (propertyName == null)
throw new ArgumentNullException(nameof(propertyName));
((IDictionary<string, object>)expandoObject).Remove(propertyName);
}
/// <summary>
/// 判断 ExpandoObject 是否为空
/// </summary>
/// <param name="expandoObject"></param>
/// <returns></returns>
public static bool Empty(this ExpandoObject expandoObject)
{
return !((IDictionary<string, object>)expandoObject).Any();
}
/// <summary>
/// 判断 ExpandoObject 是否拥有某属性
/// </summary>
/// <param name="expandoObject"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
public static bool HasProperty(this ExpandoObject expandoObject, string propertyName)
{
if (expandoObject == null)
throw new ArgumentNullException(nameof(expandoObject));
if (propertyName == null)
throw new ArgumentNullException(nameof(propertyName));
return ((IDictionary<string, object>)expandoObject).ContainsKey(propertyName);
}
/// <summary>
/// 实现 ExpandoObject 浅拷贝
/// </summary>
/// <param name="expandoObject"></param>
/// <returns></returns>
public static ExpandoObject ShallowCopy(this ExpandoObject expandoObject)
{
return Copy(expandoObject, false);
}
/// <summary>
/// 实现 ExpandoObject 深度拷贝
/// </summary>
/// <param name="expandoObject"></param>
/// <returns></returns>
public static ExpandoObject DeepCopy(this ExpandoObject expandoObject)
{
return Copy(expandoObject, true);
}
/// <summary>
/// 拷贝 ExpandoObject 对象
/// </summary>
/// <param name="original"></param>
/// <param name="deep"></param>
/// <returns></returns>
private static ExpandoObject Copy(ExpandoObject original, bool deep)
{
var clone = new ExpandoObject();
var _original = (IDictionary<string, object>)original;
var _clone = (IDictionary<string, object>)clone;
foreach (var kvp in _original)
{
_clone.Add(
kvp.Key,
deep && kvp.Value is ExpandoObject eObject ? DeepCopy(eObject) : kvp.Value
);
}
return clone;
}
}
}
ObjectExtensions
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace ClayObject.Extensions
{
/// <summary>
/// 对象拓展类
/// </summary>
public static class ObjectExtensions
{
/// <summary>
/// 将 DateTimeOffset 转换成 DateTime
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public static DateTime ConvertToDateTime(this DateTimeOffset dateTime)
{
if (dateTime.Offset.Equals(TimeSpan.Zero))
return dateTime.UtcDateTime;
else if (dateTime.Offset.Equals(TimeZoneInfo.Local.GetUtcOffset(dateTime.DateTime)))
return DateTime.SpecifyKind(dateTime.DateTime, DateTimeKind.Local);
else
return dateTime.DateTime;
}
/// <summary>
/// 将 DateTime 转换成 DateTimeOffset
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public static DateTimeOffset ConvertToDateTimeOffset(this DateTime dateTime)
{
return DateTime.SpecifyKind(dateTime, DateTimeKind.Local);
}
/// <summary>
/// 判断是否是富基元类型
/// </summary>
/// <param name="type">类型</param>
/// <returns></returns>
internal static bool IsRichPrimitive(this Type type)
{
// 处理元组类型
if (type.IsValueTuple()) return false;
// 处理数组类型,基元数组类型也可以是基元类型
if (type.IsArray) return type.GetElementType().IsRichPrimitive();
// 基元类型或值类型或字符串类型
if (type.IsPrimitive || type.IsValueType || type == typeof(string)) return true;
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) return type.GenericTypeArguments[0].IsRichPrimitive();
return false;
}
/// <summary>
/// 合并两个字典
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dic">字典</param>
/// <param name="newDic">新字典</param>
/// <returns></returns>
internal static Dictionary<string, T> AddOrUpdate<T>(this Dictionary<string, T> dic, Dictionary<string, T> newDic)
{
foreach (var key in newDic.Keys)
{
if (dic.ContainsKey(key))
dic[key] = newDic[key];
else
dic.Add(key, newDic[key]);
}
return dic;
}
/// <summary>
/// 判断是否是元组类型
/// </summary>
/// <param name="type">类型</param>
/// <returns></returns>
internal static bool IsValueTuple(this Type type)
{
return type.ToString().StartsWith(typeof(ValueTuple).FullName);
}
/// <summary>
/// 判断方法是否是异步
/// </summary>
/// <param name="method">方法</param>
/// <returns></returns>
internal static bool IsAsync(this MethodInfo method)
{
return method.ReturnType.IsAsync();
}
/// <summary>
/// 判断类型是否是异步类型
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
internal static bool IsAsync(this Type type)
{
return type.ToString().StartsWith(typeof(Task).FullName);
}
/// <summary>
/// 判断类型是否实现某个泛型
/// </summary>
/// <param name="type">类型</param>
/// <param name="generic">泛型类型</param>
/// <returns>bool</returns>
internal static bool HasImplementedRawGeneric(this Type type, Type generic)
{
// 检查接口类型
var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType);
if (isTheRawGenericType) return true;
// 检查类型
while (type != null && type != typeof(object))
{
isTheRawGenericType = IsTheRawGenericType(type);
if (isTheRawGenericType) return true;
type = type.BaseType;
}
return false;
// 判断逻辑
bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type);
}
/// <summary>
/// 判断是否是匿名类型
/// </summary>
/// <param name="obj">对象</param>
/// <returns></returns>
internal static bool IsAnonymous(this object obj)
{
var type = obj.GetType();
return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false)
&& type.IsGenericType && type.Name.Contains("AnonymousType")
&& (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"))
&& type.Attributes.HasFlag(TypeAttributes.NotPublic);
}
/// <summary>
/// 获取所有祖先类型
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
internal static IEnumerable<Type> GetAncestorTypes(this Type type)
{
var ancestorTypes = new List<Type>();
while (type != null && type != typeof(object))
{
if (IsNoObjectBaseType(type))
{
var baseType = type.BaseType;
ancestorTypes.Add(baseType);
type = baseType;
}
else break;
}
return ancestorTypes;
static bool IsNoObjectBaseType(Type type) => type.BaseType != typeof(object);
}
/// <summary>
/// 获取方法真实返回类型
/// </summary>
/// <param name="method"></param>
/// <returns></returns>
internal static Type GetRealReturnType(this MethodInfo method)
{
// 判断是否是异步方法
var isAsyncMethod = method.IsAsync();
// 获取类型返回值并处理 Task 和 Task<T> 类型返回值
var returnType = method.ReturnType;
return isAsyncMethod ? (returnType.GenericTypeArguments.FirstOrDefault() ?? typeof(void)) : returnType;
}
/// <summary>
/// 首字母大写
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
internal static string ToTitleCase(this string str)
{
return Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(str);
}
/// <summary>
/// 将一个对象转换为指定类型
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
internal static T ChangeType<T>(this object obj)
{
return (T)ChangeType(obj, typeof(T));
}
/// <summary>
/// 将一个对象转换为指定类型
/// </summary>
/// <param name="obj">待转换的对象</param>
/// <param name="type">目标类型</param>
/// <returns>转换后的对象</returns>
internal static object ChangeType(this object obj, Type type)
{
if (type == null) return obj;
if (obj == null) return type.IsValueType ? Activator.CreateInstance(type) : null;
var underlyingType = Nullable.GetUnderlyingType(type);
if (type.IsAssignableFrom(obj.GetType())) return obj;
else if ((underlyingType ?? type).IsEnum)
{
if (underlyingType != null && string.IsNullOrWhiteSpace(obj.ToString())) return null;
else return Enum.Parse(underlyingType ?? type, obj.ToString());
}
// 处理DateTime -> DateTimeOffset 类型
else if (obj.GetType().Equals(typeof(DateTime)) && (underlyingType ?? type).Equals(typeof(DateTimeOffset)))
{
return ((DateTime)obj).ConvertToDateTimeOffset();
}
// 处理 DateTimeOffset -> DateTime 类型
else if (obj.GetType().Equals(typeof(DateTimeOffset)) && (underlyingType ?? type).Equals(typeof(DateTime)))
{
return ((DateTimeOffset)obj).ConvertToDateTime();
}
else if (typeof(IConvertible).IsAssignableFrom(underlyingType ?? type))
{
try
{
return Convert.ChangeType(obj, underlyingType ?? type, null);
}
catch
{
return underlyingType == null ? Activator.CreateInstance(type) : null;
}
}
else
{
var converter = TypeDescriptor.GetConverter(type);
if (converter.CanConvertFrom(obj.GetType())) return converter.ConvertFrom(obj);
var constructor = type.GetConstructor(Type.EmptyTypes);
if (constructor != null)
{
var o = constructor.Invoke(null);
var propertys = type.GetProperties();
var oldType = obj.GetType();
foreach (var property in propertys)
{
var p = oldType.GetProperty(property.Name);
if (property.CanWrite && p != null && p.CanRead)
{
property.SetValue(o, ChangeType(p.GetValue(obj, null), property.PropertyType), null);
}
}
return o;
}
}
return obj;
}
}
}