gpt4 book ai didi

c# - 寻找一种快速简便的方法来合并 POCO 上的所有属性

转载 作者:可可西里 更新时间:2023-11-01 07:56:11 28 4
gpt4 key购买 nike

我有一些带有一堆简单属性(简单的 {get; set;} 声明)的普通类。所有属性都可以为 null(或等效地,引用类型)。

例如:

class POCO
{
int? Field1 { get; set; }
string Field2 { get; set; }
... etc ...
}

我有一个场景,我正在零碎地构建这些 POCO,最后我想要得到其中一个包含所有非空字段的

一些说明性代码:

POCO o1 = LoadFields1To3();
POCO o2 = LoadFields4To5();
POCO o3 = LoadFields6To9();
... etc ...

我们处于这种情况下,因为一些字段是从 SQL(有时是不同的查询)加载的,而另一些是从内存数据结构中加载的。我在这里重新使用 POCO 类型以避免一堆其他毫无意义的类(静态类型对 Dapper 非常有用,而且一般来说)。

我正在寻找的是一种将这些对象的属性合并为具有非空属性的单个属性的好方法。

类似于:

POCO final = o1.UnionProperties(o2).UnionProperties(o3) // and so on

我能够保证在多个对象上没有字段是非空的。虽然我假设解决方案会采用最左边的非空字段,但实际上并没有必要。

我知道我可以写一些反射代码来做到这一点,但它有点讨厌而且很慢。

这确实需要普遍适用,因为虽然我从未打算合并不同类型的对象,但此方法适用的类型非常多。

我想知道是否有更聪明的方法,也许ab使用动态?

最佳答案

我收集(好吧,我问过你)这里的主要目标是:

  • 性能(反射似乎太慢)
  • 低维护(希望避免非常手动的复制方法或复杂的属性)

以下使用元编程在运行时即时执行任何操作,将自身编译为类型化委托(delegate) ( Action<POCO, POCO> ) 以实现高效重用:

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>));
}
}
}

关于c# - 寻找一种快速简便的方法来合并 POCO 上的所有属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7422861/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com