参考属性列表
List of properties by ref
我正在尝试概括一个 C# class,它拥有多个相同类型的属性(并且它们都具有相同的实现)。
我最终想要的是删除所有属性并保留一个 Dictionary<string, type>
,它使用唯一 ID 映射每个 属性。
目前删除属性会非常费力,但可以重构一些现有功能,而不是多次“复制粘贴”到 read/update 一个 属性 ,对未来字典使用循环并更新每个键。
我怎样才能做到以下几点?
//Simplified example
class Person {
public double Height { get; set; }
public double Weight { get; set; }
public double Age { get; set; }
public double SomethingElse { get; set; }
//.. maybe more
public void CopyPasteCode()
{
Height = -1.0;
Weight = -1.0;
Age = -1.0;
SomethingElse = -1.0;
}
public void Refactored()
{
var properties = //How to do this?
new List<ref double>()
{
ref Height, ref Weight, ref Age, ref IQ
};
foreach(var p in properties)
{
p = -1.0;
}
}
}
我不会重点说明为什么您可能需要这种方法,而是展示一些实现技术。
选项 1:反射,字符串 属性 名称
使用这个辅助方法:
static void SetProperties(object obj, string[] propertyNames, object value)
{
var type = obj.GetType();
foreach (var name in propertyNames)
{
var property = type.GetProperty(name);
property.SetValue(obj, value);
}
}
你这样做:
public void Refactored()
{
var propertyNames = new[] {
nameof(Height), nameof(Weight), nameof(Age), nameof(IQ)
};
SetProperties(this, propertyNames, -0.1m);
}
选项 2:反射,强类型,用 lambdas 指定的属性
辅助方法:
static void SetProperties<TObj, TProp>(
TObj obj,
TProp value,
params Expression<Func<TObj, TProp>>[] properties)
{
foreach (var lambda in properties)
{
var property = (PropertyInfo)((MemberExpression)lambda.Body).Member;
property.SetValue(obj, value);
}
}
然后你按如下方式使用它:
public void Refactored()
{
SetProperties(
this,
-0.1m,
x => x.Height, x => x.Weight, x => x.Age, x => x.IQ);
}
使用 lambda 具有编译器类型和名称检查、重命名重构以及使用 IntelliSense 选择属性(当您键入 x => x.
时)的优势。
选项 3:Reflection.Emit
通过反射访问属性意味着性能损失,这取决于您的要求。提供与原始 CopyPasteCode()
相同性能的更快方法是使用从 IL 指令构建的动态方法,您可以即时创建一次,然后在应用程序的整个生命周期中使用。
但是,如果您是 Reflection.Emit 的新手:这是一种低级机制,需要大量学习。出于这个原因,使用可用的包装器库之一会更简单、更快(也更安全)。在这种情况下,Marc Gravell 的 fast-member 将是一个不错的选择。
结合之前的选项:
static void SetProperties<TObj, TProp>(
TObj obj,
TProp value,
params Expression<Func<TObj, TProp>>[] properties)
{
var accessor = TypeAccessor.Create(obj.GetType());
foreach (var lambda in properties)
{
var property = (PropertyInfo)((MemberExpression)lambda.Body).Member;
accessor[obj, property.Name] = value;
}
}
用法不变:
public void Refactored()
{
SetProperties(
this,
-0.1m,
x => x.Height, x => x.Weight, x => x.Age, x => x.IQ);
}
我正在尝试概括一个 C# class,它拥有多个相同类型的属性(并且它们都具有相同的实现)。
我最终想要的是删除所有属性并保留一个 Dictionary<string, type>
,它使用唯一 ID 映射每个 属性。
目前删除属性会非常费力,但可以重构一些现有功能,而不是多次“复制粘贴”到 read/update 一个 属性 ,对未来字典使用循环并更新每个键。
我怎样才能做到以下几点?
//Simplified example
class Person {
public double Height { get; set; }
public double Weight { get; set; }
public double Age { get; set; }
public double SomethingElse { get; set; }
//.. maybe more
public void CopyPasteCode()
{
Height = -1.0;
Weight = -1.0;
Age = -1.0;
SomethingElse = -1.0;
}
public void Refactored()
{
var properties = //How to do this?
new List<ref double>()
{
ref Height, ref Weight, ref Age, ref IQ
};
foreach(var p in properties)
{
p = -1.0;
}
}
}
我不会重点说明为什么您可能需要这种方法,而是展示一些实现技术。
选项 1:反射,字符串 属性 名称
使用这个辅助方法:
static void SetProperties(object obj, string[] propertyNames, object value)
{
var type = obj.GetType();
foreach (var name in propertyNames)
{
var property = type.GetProperty(name);
property.SetValue(obj, value);
}
}
你这样做:
public void Refactored()
{
var propertyNames = new[] {
nameof(Height), nameof(Weight), nameof(Age), nameof(IQ)
};
SetProperties(this, propertyNames, -0.1m);
}
选项 2:反射,强类型,用 lambdas 指定的属性
辅助方法:
static void SetProperties<TObj, TProp>(
TObj obj,
TProp value,
params Expression<Func<TObj, TProp>>[] properties)
{
foreach (var lambda in properties)
{
var property = (PropertyInfo)((MemberExpression)lambda.Body).Member;
property.SetValue(obj, value);
}
}
然后你按如下方式使用它:
public void Refactored()
{
SetProperties(
this,
-0.1m,
x => x.Height, x => x.Weight, x => x.Age, x => x.IQ);
}
使用 lambda 具有编译器类型和名称检查、重命名重构以及使用 IntelliSense 选择属性(当您键入 x => x.
时)的优势。
选项 3:Reflection.Emit
通过反射访问属性意味着性能损失,这取决于您的要求。提供与原始 CopyPasteCode()
相同性能的更快方法是使用从 IL 指令构建的动态方法,您可以即时创建一次,然后在应用程序的整个生命周期中使用。
但是,如果您是 Reflection.Emit 的新手:这是一种低级机制,需要大量学习。出于这个原因,使用可用的包装器库之一会更简单、更快(也更安全)。在这种情况下,Marc Gravell 的 fast-member 将是一个不错的选择。
结合之前的选项:
static void SetProperties<TObj, TProp>(
TObj obj,
TProp value,
params Expression<Func<TObj, TProp>>[] properties)
{
var accessor = TypeAccessor.Create(obj.GetType());
foreach (var lambda in properties)
{
var property = (PropertyInfo)((MemberExpression)lambda.Body).Member;
accessor[obj, property.Name] = value;
}
}
用法不变:
public void Refactored()
{
SetProperties(
this,
-0.1m,
x => x.Height, x => x.Weight, x => x.Age, x => x.IQ);
}