需求概述
现用户提出这样个需求,其拿到一个只读属性值(只有get方法,没有set),如何强行修改这个属性的值呢?
这里的属性可以是单值,常见的string,float,double etc,可以是List 对象集合属性。
需求实例
现对Product下的Id属性和Detail属性值进行修改。
根据对于属性原来的值和属性值类型其存在两种情况:
1 属性值为空时,单值、List集合操作为均为append追加操作;
2 属性值不为空,操作分两种:对于单值,进行modify操作;对于多值,需要定位到具体索引,同时可以通过startIndex和length来定位modify操作的长度。
class Product
{
public string Id { get;}//只读,不可写
public string Name { get; set; }
public List<ProductDetail> Detail { get; }//只读,不可写
public List<ProductComment> Comment { get; set; }
}
class ProductDetail
{
public string DtlId { get; set; }
public string Id { get; set; }
public decimal Number { get; set; }
public decimal Price { get; set; }
public decimal Amount { get; set; }
}
实现概述
传入instance对象,在ModifyFieldsValue方法中通过反射获取到指定membername的成员信息。
通过pinfo.CanWrite属性值判断其是否可写,当不可写时,通过newType.GetRuntimeFields()拿到其私有字段值。
调用SetListValueInField方法通过判断MemberInfo具体为PropertyInfo或FieldInfo,来将list数据装载到instance对应的属性上。
实现实例
static void Main(string[] args)
{
var instance = new Product();
var DetailListBefore = new List<ProductDetail>
{
new ProductDetail{Id="000" ,DtlId="1",Number=12.3568M,Price=5.689M,Amount=70.2978352M},
new ProductDetail{Id="000",DtlId="2",Number=12.35M,Price=5.689M,Amount=70.2978352M},
new ProductDetail{Id="000",DtlId="3",Number=12.358M,Price=5.689M,Amount=70.304662M},
};
//instance.Detail = DetailListBefore;
var DetailListAfter = new List<ProductDetail>
{
new ProductDetail{Id="111" ,DtlId="4",Number=12.3568M,Price=5.689M,Amount=70.2978352M},
new ProductDetail{Id="111",DtlId="5",Number=12.35M,Price=5.689M,Amount=70.2978352M},
new ProductDetail{Id="111",DtlId="6",Number=12.358M,Price=5.689M,Amount=70.304662M},
};
MethodHelper.ModifyFieldsValue<Product, string>(instance, "Id", "1000");
MethodHelper.ModifyFieldsValue<Product, ProductDetail>(instance, "Detail", null, DetailListAfter, true, 0, 3);
Console.WriteLine(instance.Id);
instance.Detail.ForEach(a => { Console.WriteLine(a.DtlId); });
Console.WriteLine("----------------------------");
Console.ReadKey();
}
//MethodHelper类
/// <summary>
/// 修改实例对应只读或可读写属性的值
/// </summary>
/// <typeparam name="T">实例对象类型</typeparam>
/// <typeparam name="S">属性类型</typeparam>
/// <param name="instance">实例对象</param>
/// <param name="membername">属性名</param>
/// <param name="Singlevalue">赋予的单值</param>
/// <param name="assignlist">赋予的List值</param>
/// <param name="appendOrReplace">对于泛型集合属性,appendOrReplace为true,单值默认为false</param>
/// <param name="StartIndex">>对于泛型集合属性,赋予list新值在原List的StartIndex索引,默认为-1</param>
/// <param name="length">索引长度,默认为0</param>
public static void ModifyFieldsValue<T, S>(T instance, string membername, object Singlevalue = null, List<S> assignlist = null, bool appendOrReplace = false, int StartIndex = -1, int length = 0)
{
var newType = instance.GetType();
var pInfo = newType.GetRuntimeProperties().FirstOrDefault(p => p.Name == membername);
if (pInfo != null && pInfo.CanWrite)
{
if (appendOrReplace)
{
List<S> fVal = (List<S>)pInfo.GetValue(instance);
//原List属性没有值时:
if (fVal == null)
{
SetListValueInField(instance, assignlist, pInfo);
}
//原List属性存在值时:
else
{
AppendOrReplaceList(assignlist, StartIndex, length, fVal);
pInfo.SetValue(instance, fVal);
}
}
else
{
//(S)Convert.ChangeType(Singlevalue, typeof(S))
pInfo.SetValue(instance, Singlevalue);
}
}
else if (pInfo != null)
{
var backFildInfo = newType.GetRuntimeFields().FirstOrDefault(f => f.Name.Contains($"<{membername}>") && f.Name.Contains("BackingField"));
if (appendOrReplace)
{
List<S> fVal = (List<S>)backFildInfo.GetValue(instance);
if (fVal == null)
{
SetListValueInField(instance, assignlist, backFildInfo);
}
else
{
AppendOrReplaceList(assignlist, StartIndex, length, fVal);
}
}
else
{
backFildInfo.SetValue(instance, Singlevalue);
}
}
else
{
return;
}
}
private static void AppendOrReplaceList<S>(List<S> assignlist, int StartIndex, int length, List<S> fVal)
{
if (fVal.Count < length || fVal.Count < assignlist.Count)
{
throw new Exception("传入的length值或assignlist不对,其应小于或等于原集合长度");
}
if (StartIndex != -1 && length > 0)
{
for (int index = 0; index < length; index++)
{
fVal[StartIndex + index] = assignlist[index];
}
}
}
private static void SetListValueInField<T, S>(T instance, List<S> assginlist, MemberInfo pInfo)
{
var type1 = typeof(List<>);
if (pInfo is PropertyInfo)
{
type1 = type1.MakeGenericType((pInfo as PropertyInfo).PropertyType.GenericTypeArguments.First());
object instanceVal = CreateInstanceWithList(assginlist, type1);
(pInfo as PropertyInfo).SetValue(instance, instanceVal);
}
if (pInfo is FieldInfo)
{
type1 = type1.MakeGenericType(((pInfo as FieldInfo)).FieldType.GenericTypeArguments.First());
object instanceVal = CreateInstanceWithList(assginlist, type1);
(pInfo as FieldInfo).SetValue(instance, instanceVal);
}
}
private static object CreateInstanceWithList<S>(List<S> assginlist, Type type1)
{
var instanceVal = Activator.CreateInstance(type1);
var add = type1.GetMethod("Add", type1.GetGenericArguments());
foreach (var obj in assginlist)
{
List<object> parametersList = new List<object>();
parametersList.Add(obj);
add.Invoke(instanceVal, parametersList.ToArray());
}
return instanceVal;
}