寻找一个快速简便的方法凝聚在POCO的所有属性 [英] Looking for a fast and easy way to coalesce all properties on a POCO
问题描述
我有一些纯老班一束简单的属性(简单的 {获取;设置;}
声明)。所有属性都可以为空(或等价,引用类型)。
I've got some plain-old classes with a bunch of simple properties (simple {get; set;}
declarations). All the properties are nullable (or equivalently, reference types).
例如:
class POCO
{
int? Field1 { get; set; }
string Field2 { get; set; }
... etc ...
}
我有在那里我零零碎碎建设这些波苏斯一个场景,并在结束时,我想获得的其中之一的所有非空领域。
一些示例code:
POCO o1 = LoadFields1To3();
POCO o2 = LoadFields4To5();
POCO o3 = LoadFields6To9();
... etc ...
我们是在这种情况下,因为有些领域是从SQL(有时是不同的查询)加载,而另一些则在内存中的数据结构的加载。我在这里的POCO类型,避免了一堆毫无意义的,否则类(静态类型为对小巧玲珑的非常有用的,只是一般的)重新使用。
We're in this scenario because some of the fields are loaded from SQL (and sometimes distinct queries), while some are loaded from in memory data structures. I'm re-using the POCO type here to avoid a bunch of otherwise pointless classes (a static type being quite useful for Dapper, and just in general).
我正在寻找的是凝聚这些对象的属性到一个单一的一个具有非空属性的好方法。
What I'm looking for is a nice way to coalesce these objects' properties into a single one with the non-null properties.
是这样的:
POCO final = o1.UnionProperties(o2).UnionProperties(o3) // and so on
我可以保证没有场非空的多个对象。虽然我认为一个解决方案将采取最左边的非空领域,它实际上并不是必要的。
I am able to guarantee that no field is non-null on more than one object. Though I'd assume a solution would take the left-most non-null field, it's not actually necessary.
我知道我可以写一些反映code要做到这一点,但它是一个有点讨厌的和缓慢的。
I know I can write some reflection code to do this, but it's a bit nasty and slow.
此并不需要是一般适用,而我从不打算合并不同类型的对象中,有一个非常大的数量的类型,这种方法将适用于
This does need to be generically applicable, as while I never intend to coalesce objects of different types, there are a very large number of types that this method would be applicable to.
我在想,如果没有一些聪明的方式,也许是 AB 使用动态?
I was wondering if there isn't some cleverer way, perhaps abusing dynamic?
推荐答案
我推测(好吧,我问你),这里的主要目标是:
I gather (ok, I asked you) that the key objectives here are:
- 性能(反射似乎太慢了)
- 低维护(要避免一个非常手动复制法,或复杂的属性)
下面使用的元编程来尽其所能在运行时动态,编制本身类型化的委托(动作< POCO,POCO>
)的高效重 - 使用:
The following uses meta-programming to do whatever it can on the fly at runtime, compiling itself to a typed delegate (Action<POCO, POCO>
) for efficient re-use:
using System;
using System.Collections.Generic;
using System.Reflection.Emit;
public class SamplePoco
{
public int? Field1 { get; set; }
public string Field2 { get; set; }
// lots and lots more properties here
}
static class Program
{
static void Main()
{
var obj1 = new SamplePoco { Field1 = 123 };
var obj2 = new SamplePoco { Field2 = "abc" };
var merged = Merger.Merge(obj1, obj2);
Console.WriteLine(merged.Field1);
Console.WriteLine(merged.Field2);
}
}
static class Merger
{
public static T Merge<T>(params T[] sources) where T : class, new()
{
var merge = MergerImpl<T>.merge;
var obj = new T();
for (int i = 0; i < sources.Length; i++) merge(sources[i], obj);
return obj;
}
static class MergerImpl<T> where T : class, new()
{
internal static readonly Action<T, T> merge;
static MergerImpl()
{
var method = new DynamicMethod("Merge", null, new[] { typeof(T), typeof(T) }, typeof(T));
var il = method.GetILGenerator();
Dictionary<Type, LocalBuilder> locals = new Dictionary<Type, LocalBuilder>();
foreach (var prop in typeof(T).GetProperties())
{
var propType = prop.PropertyType;
if (propType.IsValueType && Nullable.GetUnderlyingType(propType) == null)
{
continue; // int, instead of int? etc - skip
}
il.Emit(OpCodes.Ldarg_1); // [target]
il.Emit(OpCodes.Ldarg_0); // [target][source]
il.EmitCall(OpCodes.Callvirt, prop.GetGetMethod(), null); // [target][value]
il.Emit(OpCodes.Dup); // [target][value][value]
Label nonNull = il.DefineLabel(), end = il.DefineLabel();
if (propType.IsValueType)
{ // int? etc - Nullable<T> - hit .Value
LocalBuilder local;
if (!locals.TryGetValue(propType, out local))
{
local = il.DeclareLocal(propType);
locals.Add(propType, local);
}
// need a ref to use it for the static-call
il.Emit(OpCodes.Stloc, local); // [target][value]
il.Emit(OpCodes.Ldloca, local); // [target][value][value*]
var hasValue = propType.GetProperty("HasValue").GetGetMethod();
il.EmitCall(OpCodes.Call, hasValue, null); // [target][value][value.HasValue]
}
il.Emit(OpCodes.Brtrue_S, nonNull); // [target][value]
il.Emit(OpCodes.Pop); // [target]
il.Emit(OpCodes.Pop); // nix
il.Emit(OpCodes.Br_S, end); // nix
il.MarkLabel(nonNull); // (incoming) [target][value]
il.EmitCall(OpCodes.Callvirt, prop.GetSetMethod(), null); // nix
il.MarkLabel(end); // (incoming) nix
}
il.Emit(OpCodes.Ret);
merge = (Action<T, T>)method.CreateDelegate(typeof(Action<T, T>));
}
}
}
这篇关于寻找一个快速简便的方法凝聚在POCO的所有属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!