Skip to content

Commit

Permalink
!11 将反射赋值优化为表达式树赋值
Browse files Browse the repository at this point in the history
Merge pull request !11 from 若汝棋茗/master
  • Loading branch information
shps951023 authored and gitee-org committed Jun 19, 2023
2 parents 7850be8 + 81d6022 commit 6517fb2
Show file tree
Hide file tree
Showing 6 changed files with 259 additions and 14 deletions.
15 changes: 15 additions & 0 deletions src/MiniExcel/Reflection/Member.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MiniExcelLibs
{
/// <summary>
/// 用于表达式树的成员
/// </summary>
public abstract class Member
{
}
}
66 changes: 66 additions & 0 deletions src/MiniExcel/Reflection/MemberGetter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Linq.Expressions;
using System.Reflection;

namespace MiniExcelLibs
{
/// <summary>
/// 表示属性的Getter
/// </summary>
public class MemberGetter
{
/// <summary>
/// get方法委托
/// </summary>
private readonly Func<object, object> m_getFunc;

/// <summary>
/// 表示属性的Getter
/// </summary>
/// <param name="property">属性</param>
/// <exception cref="ArgumentNullException"></exception>
public MemberGetter(PropertyInfo property)
{
m_getFunc = CreateGetterDelegate(property);
}

/// <summary>
/// 表示类型字段或属性的Getter
/// </summary>
/// <exception cref="ArgumentNullException"></exception>
public MemberGetter(FieldInfo fieldInfo)
{
m_getFunc = CreateGetterDelegate(fieldInfo);
}

/// <summary>
/// 获取属性的值
/// </summary>
/// <param name="instance">实例</param>
/// <returns></returns>
public object Invoke(object instance)
{
return m_getFunc.Invoke(instance);
}

private static Func<object, object> CreateGetterDelegate(PropertyInfo property)
{
var param_instance = Expression.Parameter(typeof(object));
var body_instance = Expression.Convert(param_instance, property.DeclaringType);
var body_property = Expression.Property(body_instance, property);
var body_return = Expression.Convert(body_property, typeof(object));

return Expression.Lambda<Func<object, object>>(body_return, param_instance).Compile();
}

private static Func<object, object> CreateGetterDelegate(FieldInfo fieldInfo)
{
var param_instance = Expression.Parameter(typeof(object));
var body_instance = Expression.Convert(param_instance, fieldInfo.DeclaringType);
var body_field = Expression.Field(body_instance, fieldInfo);
var body_return = Expression.Convert(body_field, typeof(object));

return Expression.Lambda<Func<object, object>>(body_return, param_instance).Compile();
}
}
}
54 changes: 54 additions & 0 deletions src/MiniExcel/Reflection/MemberSetter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Linq.Expressions;
using System.Reflection;

namespace MiniExcelLibs
{
/// <summary>
/// 表示属性的设置器
/// </summary>
public class MemberSetter
{
/// <summary>
/// set方法委托
/// </summary>
private readonly Action<object, object> setFunc;

/// <summary>
/// 表示属性的Getter
/// </summary>
/// <param name="property">属性</param>
/// <exception cref="ArgumentNullException"></exception>
public MemberSetter(PropertyInfo property)
{
if (property == null)
{
throw new ArgumentNullException(nameof(property));
}
setFunc = CreateSetterDelegate(property);
}

/// <summary>
/// 设置属性的值
/// </summary>
/// <param name="instance">实例</param>
/// <param name="value">值</param>
/// <returns></returns>
public void Invoke(object instance, object value)
{
setFunc.Invoke(instance, value);
}

private static Action<object, object> CreateSetterDelegate(PropertyInfo property)
{
var param_instance = Expression.Parameter(typeof(object));
var param_value = Expression.Parameter(typeof(object));

var body_instance = Expression.Convert(param_instance, property.DeclaringType);
var body_value = Expression.Convert(param_value, property.PropertyType);
var body_call = Expression.Call(body_instance, property.GetSetMethod(true), body_value);

return Expression.Lambda<Action<object, object>>(body_call, param_instance, param_value).Compile();
}
}
}
110 changes: 110 additions & 0 deletions src/MiniExcel/Reflection/Property.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@

using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;

namespace MiniExcelLibs
{
/// <summary>
/// 表示属性
/// </summary>
public class Property: Member
{
/// <summary>
/// 类型属性的Setter缓存
/// </summary>
private static readonly ConcurrentDictionary<Type, Property[]> m_cached = new ConcurrentDictionary<Type, Property[]>();

/// <summary>
/// 获取器
/// </summary>
private readonly MemberGetter m_geter;

/// <summary>
/// 设置器
/// </summary>
private readonly MemberSetter m_seter;

/// <summary>
/// 属性
/// </summary>
/// <param name="property">属性信息</param>
public Property(PropertyInfo property)
{
Name = property.Name;
Info = property;

if (property.CanRead == true)
{
CanRead = true;
m_geter = new MemberGetter(property);
}
if (property.CanWrite == true)
{
CanWrite = true;
m_seter = new MemberSetter(property);
}
}

/// <summary>
/// 是否可以读取
/// </summary>
public bool CanRead { get; private set; }

/// <summary>
/// 是否可以写入
/// </summary>
public bool CanWrite { get; private set; }

/// <summary>
/// 获取属性信息
/// </summary>
public PropertyInfo Info { get; private set; }

/// <summary>
/// 获取属性名称
/// </summary>
public string Name { get; protected set; }

/// <summary>
/// 从类型的属性获取属性
/// </summary>
/// <param name="type">类型</param>
/// <returns></returns>
public static Property[] GetProperties(Type type)
{
return m_cached.GetOrAdd(type, t => t.GetProperties().Select(p => new Property(p)).ToArray());
}

/// <summary>
/// 获取属性的值
/// </summary>
/// <param name="instance">实例</param>
/// <exception cref="NotSupportedException"></exception>
/// <returns></returns>
public object GetValue(object instance)
{
if (m_geter == null)
{
throw new NotSupportedException();
}
return m_geter.Invoke(instance);
}

/// <summary>
/// 设置属性的值
/// </summary>
/// <param name="instance">实例</param>
/// <param name="value">值</param>
/// <exception cref="NotSupportedException"></exception>
public void SetValue(object instance, object value)
{
if (m_seter == null)
{
throw new NotSupportedException($"{Name}不允许赋值");
}
m_seter.Invoke(instance, value);
}
}
}
14 changes: 7 additions & 7 deletions src/MiniExcel/Utils/CustomPropertyHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal class ExcelColumnInfo
public int? ExcelColumnIndex { get; set; }
public string ExcelColumnName { get; set; }
public string[] ExcelColumnAliases { get; set; }
public PropertyInfo Property { get; set; }
public Property Property { get; set; }
public Type ExcludeNullableType { get; set; }
public bool Nullable { get; internal set; }
public string ExcelFormat { get; internal set; }
Expand Down Expand Up @@ -49,7 +49,7 @@ internal static IDictionary<string, object> GetEmptyExpandoObject(Dictionary<int
internal static List<ExcelColumnInfo> GetSaveAsProperties(this Type type, Configuration configuration)
{
List<ExcelColumnInfo> props = GetExcelPropertyInfo(type, BindingFlags.Public | BindingFlags.Instance, configuration)
.Where(prop => prop.Property.GetGetMethod() != null)
.Where(prop => prop.Property.CanRead)
.ToList() /*ignore without set*/;

if (props.Count == 0)
Expand Down Expand Up @@ -103,9 +103,9 @@ internal static List<ExcelColumnInfo> SortCustomProps(List<ExcelColumnInfo> prop
internal static List<ExcelColumnInfo> GetExcelCustomPropertyInfos(Type type, string[] keys, Configuration configuration)
{
List<ExcelColumnInfo> props = GetExcelPropertyInfo(type, BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance, configuration)
.Where(prop => prop.Property.GetSetMethod() != null
&& !prop.Property.GetAttributeValue((ExcelIgnoreAttribute x) => x.ExcelIgnore)
&& !prop.Property.GetAttributeValue((ExcelColumnAttribute x) => x.Ignore))
.Where(prop => prop.Property.CanWrite
&& !prop.Property.Info.GetAttributeValue((ExcelIgnoreAttribute x) => x.ExcelIgnore)
&& !prop.Property.Info.GetAttributeValue((ExcelColumnAttribute x) => x.Ignore))
.ToList() /*ignore without set*/;

if (props.Count == 0)
Expand All @@ -124,7 +124,7 @@ internal static List<ExcelColumnInfo> GetExcelCustomPropertyInfos(Type type, str
if (p.ExcelColumnIndex > maxIndex)
throw new ArgumentException($"ExcelColumnIndex {p.ExcelColumnIndex} over haeder max index {maxkey}");
if (p.ExcelColumnName == null)
throw new InvalidOperationException($"{p.Property.DeclaringType.Name} {p.Property.Name}'s ExcelColumnIndex {p.ExcelColumnIndex} can't find excel column name");
throw new InvalidOperationException($"{p.Property.Info.DeclaringType.Name} {p.Property.Name}'s ExcelColumnIndex {p.ExcelColumnIndex} can't find excel column name");
}
}
}
Expand Down Expand Up @@ -172,7 +172,7 @@ private static IEnumerable<ExcelColumnInfo> ConvertToExcelCustomPropertyInfo(Pro
var excelColumnIndex = excelColumn?.Index > -1 ? excelColumn.Index : (int?)null;
return new ExcelColumnInfo
{
Property = p,
Property = new Property(p),
ExcludeNullableType = excludeNullableType,
Nullable = gt != null,
ExcelColumnAliases = excelColumnName?.Aliases ?? excelColumn?.Aliases,
Expand Down
14 changes: 7 additions & 7 deletions src/MiniExcel/Utils/TypeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public static bool IsNumericType(Type type, bool isNullableUnderlyingType = fals
var columnName = pInfo.ExcelColumnName ?? pInfo.Property.Name;
var startRowIndex = ReferenceHelper.ConvertCellToXY(startCell).Item2;
var errorRow = startRowIndex + rowIndex + 1;
throw new ExcelInvalidCastException(columnName, errorRow, itemValue, pInfo.Property.PropertyType, $"ColumnName : {columnName}, CellRow : {errorRow}, Value : {itemValue}, it can't cast to {pInfo.Property.PropertyType.Name} type.");
throw new ExcelInvalidCastException(columnName, errorRow, itemValue, pInfo.Property.Info.PropertyType, $"ColumnName : {columnName}, CellRow : {errorRow}, Value : {itemValue}, it can't cast to {pInfo.Property.Info.PropertyType.Name} type.");
}
}

Expand Down Expand Up @@ -108,7 +108,7 @@ public static bool IsNumericType(Type type, bool isNullableUnderlyingType = fals
var vs = itemValue?.ToString();
if (pInfo.ExcelFormat != null)
{
if (pInfo.Property.PropertyType == typeof(DateTimeOffset) && DateTimeOffset.TryParseExact(vs, pInfo.ExcelFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out var _v2))
if (pInfo.Property.Info.PropertyType == typeof(DateTimeOffset) && DateTimeOffset.TryParseExact(vs, pInfo.ExcelFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out var _v2))
{
newValue = _v2;
}
Expand Down Expand Up @@ -136,17 +136,17 @@ public static bool IsNumericType(Type type, bool isNullableUnderlyingType = fals
else
newValue = bool.Parse(vs);
}
else if (pInfo.Property.PropertyType == typeof(string))
else if (pInfo.Property.Info.PropertyType == typeof(string))
{
newValue = XmlEncoder.DecodeString(itemValue?.ToString());
}
else if (pInfo.Property.PropertyType.IsEnum)
else if (pInfo.Property.Info.PropertyType.IsEnum)
{
var fieldInfo = pInfo.Property.PropertyType.GetFields().FirstOrDefault(e => e.GetCustomAttribute<DescriptionAttribute>(false)?.Description == itemValue?.ToString());
var fieldInfo = pInfo.Property.Info.PropertyType.GetFields().FirstOrDefault(e => e.GetCustomAttribute<DescriptionAttribute>(false)?.Description == itemValue?.ToString());
if (fieldInfo != null)
newValue = Enum.Parse(pInfo.Property.PropertyType, fieldInfo.Name, true);
newValue = Enum.Parse(pInfo.Property.Info.PropertyType, fieldInfo.Name, true);
else
newValue = Enum.Parse(pInfo.Property.PropertyType, itemValue?.ToString(), true);
newValue = Enum.Parse(pInfo.Property.Info.PropertyType, itemValue?.ToString(), true);
}
else
{
Expand Down

0 comments on commit 6517fb2

Please sign in to comment.