给定一个包含35个字段的类和2个具有一定数量的不同字段值的对象。 是否有一种聪明的方法来获取列表< String>使用字段名称,对象如下所示?
e.g。
obj1.Name = "aaa";
obj1.LastName = "bbb";
obj1.Address = "xcs";
obj2.Name = "aaa";
obj2.LastName = "ccc";
obj2.Address = "jk";
目标:
list<<String>String> containing 2 Strings LastName and Address
我认为反思是要走的路,但有35个领域,恐怕太重了。 还有其他想法,比如linq?
答案 0 :(得分:35)
行;这是疯狂比我通常要做的更多的努力,但这可能是一个有用的实用方法。它动态创建IL(缓存)来完成工作,处理值类型vs ref类型对象,内置IL等式,等式运算符(==
)和其余EqualityComparer<T>
:
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace ConsoleApplication2
{
using System;
class Program
{
static void Main()
{
WriteDeltas(new Foo {X = 123, Y = DateTime.Today, Z = null},
new Foo {X = 124, Y = DateTime.Today, Z = null});
WriteDeltas(new Foo { X = 123, Y = DateTime.Today, Z = null },
new Foo { X = 123, Y = DateTime.Now, Z = new Dummy()});
WriteDeltas(new Bar { X = 123, Y = DateTime.Today, Z = null },
new Bar { X = 124, Y = DateTime.Today, Z = null });
WriteDeltas(new Bar { X = 123, Y = DateTime.Today, Z = null },
new Bar { X = 123, Y = DateTime.Now, Z = new Dummy() });
}
static void WriteDeltas<T>(T x, T y)
{
Console.WriteLine("----");
foreach(string delta in PropertyComparer<T>.GetDeltas(x,y))
{
Console.WriteLine(delta);
}
}
}
class Dummy {}
class Foo
{
public int X { get; set; }
public DateTime Y { get; set; }
public Dummy Z { get; set; }
}
struct Bar
{
public int X { get; set; }
public DateTime Y { get; set; }
public Dummy Z { get; set; }
}
public static class PropertyComparer<T>
{
private static readonly Func<T, T, List<string>> getDeltas;
static PropertyComparer()
{
var dyn = new DynamicMethod(":getDeltas", typeof (List<string>), new[] {typeof (T), typeof (T)},typeof(T));
var il = dyn.GetILGenerator();
il.Emit(OpCodes.Newobj, typeof (List<string>).GetConstructor(Type.EmptyTypes));
bool isValueType = typeof (T).IsValueType;
OpCode callType = isValueType ? OpCodes.Call : OpCodes.Callvirt;
var add = typeof(List<string>).GetMethod("Add");
foreach (var prop in typeof(T).GetProperties())
{
if (!prop.CanRead) continue;
Label next = il.DefineLabel();
switch (Type.GetTypeCode(prop.PropertyType))
{
case TypeCode.Boolean:
case TypeCode.Byte:
case TypeCode.Char:
case TypeCode.Double:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.SByte:
case TypeCode.Single:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
if(isValueType) {il.Emit(OpCodes.Ldarga_S, (byte)0);} else {il.Emit(OpCodes.Ldarg_0);}
il.EmitCall(callType, prop.GetGetMethod(), null);
if (isValueType) { il.Emit(OpCodes.Ldarga_S, (byte)1); } else { il.Emit(OpCodes.Ldarg_1); }
il.EmitCall(callType, prop.GetGetMethod(), null);
il.Emit(OpCodes.Ceq);
break;
default:
var pp = new Type[] {prop.PropertyType, prop.PropertyType};
var eq = prop.PropertyType.GetMethod("op_Equality", BindingFlags.Public | BindingFlags.Static, null, pp, null);
if (eq != null)
{
if (isValueType) { il.Emit(OpCodes.Ldarga_S, (byte)0); } else { il.Emit(OpCodes.Ldarg_0); }
il.EmitCall(callType, prop.GetGetMethod(), null);
if (isValueType) { il.Emit(OpCodes.Ldarga_S, (byte)1); } else { il.Emit(OpCodes.Ldarg_1); }
il.EmitCall(callType, prop.GetGetMethod(), null);
il.EmitCall(OpCodes.Call, eq, null);
}
else
{
il.EmitCall(OpCodes.Call, typeof(EqualityComparer<>).MakeGenericType(prop.PropertyType).GetProperty("Default").GetGetMethod(), null);
if (isValueType) { il.Emit(OpCodes.Ldarga_S, (byte)0); } else { il.Emit(OpCodes.Ldarg_0); }
il.EmitCall(callType, prop.GetGetMethod(), null);
if (isValueType) { il.Emit(OpCodes.Ldarga_S, (byte)1); } else { il.Emit(OpCodes.Ldarg_1); }
il.EmitCall(callType, prop.GetGetMethod(), null);
il.EmitCall(OpCodes.Callvirt, typeof(EqualityComparer<>).MakeGenericType(prop.PropertyType).GetMethod("Equals", pp), null);
}
break;
}
il.Emit(OpCodes.Brtrue_S, next); // equal
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldstr, prop.Name);
il.EmitCall(OpCodes.Callvirt, add, null);
il.MarkLabel(next);
}
il.Emit(OpCodes.Ret);
getDeltas = (Func<T, T, List<string>>)dyn.CreateDelegate(typeof (Func<T, T, List<string>>));
}
public static List<string> GetDeltas(T x, T y) { return getDeltas(x, y); }
}
}
答案 1 :(得分:1)
反思就是采用这种方式,我不认为35个字段是个问题。
(在完全迷惑自己之后,我回过头来以为我理解这个问题并且反思对此很好)。