在两个 class 实例之间动态复制某些属性
Dynamically copy certain properties between two class instances
我正在尝试编写一段代码,可以获取同一对象的两个实例,并动态地将某些属性从第一个实例复制到第二个实例。有点不同的是,我只能通过它们都继承的接口访问对象。
我创建了一个 Copyable
属性,用于标记哪些属性可以被复制。
然后我设法使用 PropertyInfo.GetMethod
和 PropertyInfo.SetMethod
成功地做到了这一点,但是生成的代码 太慢了 。与在编译时静态分配属性相比 - 这种方法慢 ~20 倍。
这是我使用纯反射的初步实现。
using System;
using System.Linq;
namespace ConsoleApp58
{
interface IInterface
{
int Id { get; set; }
}
[AttributeUsage(AttributeTargets.Property)]
class CopyableAttribute : Attribute { }
class Child : IInterface
{
public int Id { get; set; }
[Copyable]
public int CopyableProp { get; set; }
}
class Program
{
static void Main(string[] args)
{
var source = new Child() {Id = 1, CopyableProp = 42};
var target = new Child() {Id = 2, CopyableProp = 0};
CopyProps(source, target);
}
static void CopyProps(IInterface source, IInterface target)
{
var props = target.GetType()
.GetProperties()
.Where(p => p.IsDefined(typeof(CopyableAttribute), false))
.ToArray();
foreach (var prop in props)
{
var value = prop.GetMethod.Invoke(source, new object[] { });
prop.SetMethod.Invoke(target, new [] {value});
}
}
}
}
这行得通,但它很慢,所以我决定尝试创建一个表达式树来构建一个可以调用 getter 和 setter 的 lambda,但是我做不到似乎让它起作用。
我尝试遵循 this SO question,但是,该实现依赖于我知道我从中获取属性的对象的类型这一事实。
但是,在我的例子中,这些属性被定义为子 类 的一部分,我无法在我的 IInterface
中访问它们。
因此,我在这里问。 有没有一种(快速)方法可以让我在两个对象的实例之间复制特定属性的值,仅通过它们的公共接口引用它们。
您可以通过表达式API生成Action<IInterface, IInterface>
。试试这个代码:
private static Expression<Action<IInterface, IInterface>> CreateCopyMethod(Type type)
{
var props = type
.GetProperties()
.Where(p => p.IsDefined(typeof(CopyableAttribute), false))
.ToArray();
var s = Expression.Parameter(typeof(IInterface), "s");
var t = Expression.Parameter(typeof(IInterface), "t");
var source = Expression.Variable(type, "source");
var castToSource = Expression.Assign(source, Expression.Convert(s, type));
var target = Expression.Variable(type, "target");
var castToTarget = Expression.Assign(target, Expression.Convert(t, type));
var instructions = new List<Expression>
{
castToSource, castToTarget
};
foreach (var property in props)
{
var left = Expression.Property(target, property);
var right = Expression.Property(source, property);
var assign = Expression.Assign(left, right);
instructions.Add(assign);
}
var lambda = Expression.Lambda<Action<IInterface, IInterface>>(
Expression.Block(
new[] {source, target}, instructions),
s, t);
return lambda;
}
用法
IInterface src = new Child
{
CopyableProp = 42
};
IInterface dst = new Child();
var copy = CreateCopyMethod(src.GetType()).Compile();
copy(src, dst);
Console.WriteLine(((Child)dst).CopyableProp); // 42
要提高性能,请考虑使用 Dictionary<Type, Action<IInterface, IInterface>>
来缓存已生成方法的实现
我正在尝试编写一段代码,可以获取同一对象的两个实例,并动态地将某些属性从第一个实例复制到第二个实例。有点不同的是,我只能通过它们都继承的接口访问对象。
我创建了一个 Copyable
属性,用于标记哪些属性可以被复制。
然后我设法使用 PropertyInfo.GetMethod
和 PropertyInfo.SetMethod
成功地做到了这一点,但是生成的代码 太慢了 。与在编译时静态分配属性相比 - 这种方法慢 ~20 倍。
这是我使用纯反射的初步实现。
using System;
using System.Linq;
namespace ConsoleApp58
{
interface IInterface
{
int Id { get; set; }
}
[AttributeUsage(AttributeTargets.Property)]
class CopyableAttribute : Attribute { }
class Child : IInterface
{
public int Id { get; set; }
[Copyable]
public int CopyableProp { get; set; }
}
class Program
{
static void Main(string[] args)
{
var source = new Child() {Id = 1, CopyableProp = 42};
var target = new Child() {Id = 2, CopyableProp = 0};
CopyProps(source, target);
}
static void CopyProps(IInterface source, IInterface target)
{
var props = target.GetType()
.GetProperties()
.Where(p => p.IsDefined(typeof(CopyableAttribute), false))
.ToArray();
foreach (var prop in props)
{
var value = prop.GetMethod.Invoke(source, new object[] { });
prop.SetMethod.Invoke(target, new [] {value});
}
}
}
}
这行得通,但它很慢,所以我决定尝试创建一个表达式树来构建一个可以调用 getter 和 setter 的 lambda,但是我做不到似乎让它起作用。
我尝试遵循 this SO question,但是,该实现依赖于我知道我从中获取属性的对象的类型这一事实。
但是,在我的例子中,这些属性被定义为子 类 的一部分,我无法在我的 IInterface
中访问它们。
因此,我在这里问。 有没有一种(快速)方法可以让我在两个对象的实例之间复制特定属性的值,仅通过它们的公共接口引用它们。
您可以通过表达式API生成Action<IInterface, IInterface>
。试试这个代码:
private static Expression<Action<IInterface, IInterface>> CreateCopyMethod(Type type)
{
var props = type
.GetProperties()
.Where(p => p.IsDefined(typeof(CopyableAttribute), false))
.ToArray();
var s = Expression.Parameter(typeof(IInterface), "s");
var t = Expression.Parameter(typeof(IInterface), "t");
var source = Expression.Variable(type, "source");
var castToSource = Expression.Assign(source, Expression.Convert(s, type));
var target = Expression.Variable(type, "target");
var castToTarget = Expression.Assign(target, Expression.Convert(t, type));
var instructions = new List<Expression>
{
castToSource, castToTarget
};
foreach (var property in props)
{
var left = Expression.Property(target, property);
var right = Expression.Property(source, property);
var assign = Expression.Assign(left, right);
instructions.Add(assign);
}
var lambda = Expression.Lambda<Action<IInterface, IInterface>>(
Expression.Block(
new[] {source, target}, instructions),
s, t);
return lambda;
}
用法
IInterface src = new Child
{
CopyableProp = 42
};
IInterface dst = new Child();
var copy = CreateCopyMethod(src.GetType()).Compile();
copy(src, dst);
Console.WriteLine(((Child)dst).CopyableProp); // 42
要提高性能,请考虑使用 Dictionary<Type, Action<IInterface, IInterface>>
来缓存已生成方法的实现