gpt4 book ai didi

c# - 比较来自 Type.GetProperties() 和 lambda 表达式的 PropertyInfo

转载 作者:可可西里 更新时间:2023-11-01 08:45:10 25 4
gpt4 key购买 nike

在创建我的测试框架时,我发现了一个奇怪的问题。

我想创建一个静态类,允许我通过属性比较相同类型的对象,但有可能忽略其中的一些对象。

我想为此提供一个简单流畅的 API,所以调用 TestEqualityComparer.Equals(first.Ignore(x=>x.Id).Ignore(y=>y.Name), second);如果给定对象在除 IdName 之外的每个属性上都相等, 将返回 true(不会检查它们是否相等)。

这是我的代码。当然,这是一个微不足道的例子(缺少一些明显方法重载),但我想尽可能提取最简单的代码。真实案例场景有点复杂,所以我真的不想改变方法。

FindProperty 方法几乎是来自 AutoMapper library 的复制粘贴.

流式 API 的对象包装器:

public class TestEqualityHelper<T>
{
public List<PropertyInfo> IgnoredProps = new List<PropertyInfo>();
public T Value;
}

流利的东西:

public static class FluentExtension
{
//Extension method to speak fluently. It finds the property mentioned
// in 'ignore' parameter and adds it to the list.
public static TestEqualityHelper<T> Ignore<T>(this T value,
Expression<Func<T, object>> ignore)
{
var eh = new TestEqualityHelper<T> { Value = value };

//Mind the magic here!
var member = FindProperty(ignore);
eh.IgnoredProps.Add((PropertyInfo)member);
return eh;
}

//Extract the MemberInfo from the given lambda
private static MemberInfo FindProperty(LambdaExpression lambdaExpression)
{
Expression expressionToCheck = lambdaExpression;

var done = false;

while (!done)
{
switch (expressionToCheck.NodeType)
{
case ExpressionType.Convert:
expressionToCheck
= ((UnaryExpression)expressionToCheck).Operand;
break;
case ExpressionType.Lambda:
expressionToCheck
= ((LambdaExpression)expressionToCheck).Body;
break;
case ExpressionType.MemberAccess:
var memberExpression
= (MemberExpression)expressionToCheck;

if (memberExpression.Expression.NodeType
!= ExpressionType.Parameter &&
memberExpression.Expression.NodeType
!= ExpressionType.Convert)
{
throw new Exception("Something went wrong");
}

return memberExpression.Member;
default:
done = true;
break;
}
}

throw new Exception("Something went wrong");
}
}

实际比较器:

public static class TestEqualityComparer
{
public static bool MyEquals<T>(TestEqualityHelper<T> a, T b)
{
return DoMyEquals(a.Value, b, a.IgnoredProps);
}

private static bool DoMyEquals<T>(T a, T b,
IEnumerable<PropertyInfo> ignoredProperties)
{
var t = typeof(T);
IEnumerable<PropertyInfo> props;

if (ignoredProperties != null && ignoredProperties.Any())
{
//THE PROBLEM IS HERE!
props =
t.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Except(ignoredProperties);
}
else
{
props =
t.GetProperties(BindingFlags.Instance | BindingFlags.Public);
}
return props.All(f => f.GetValue(a, null).Equals(f.GetValue(b, null)));
}
}

基本上就是这样。

这里有两个测试片段,第一个有效,第二个失败:

//These are the simple objects we'll compare
public class Base
{
public decimal Id { get; set; }
public string Name { get; set; }
}
public class Derived : Base
{ }

[TestMethod]
public void ListUsers()
{
//TRUE
var f = new Base { Id = 5, Name = "asdas" };
var s = new Base { Id = 6, Name = "asdas" };
Assert.IsTrue(TestEqualityComparer.MyEquals(f.Ignore(x => x.Id), s));

//FALSE
var f2 = new Derived { Id = 5, Name = "asdas" };
var s2 = new Derived { Id = 6, Name = "asdas" };
Assert.IsTrue(TestEqualityComparer.MyEquals(f2.Ignore(x => x.Id), s2));
}

问题出在 DoMyEquals 中的 Except 方法。

FindProperty 返回的属性不等于 Type.GetProperties 返回的属性。我发现的区别在于 PropertyInfo.ReflectedType

  • 无论我的对象是什么类型,FindProperty 都会告诉我反射类型是 Base

  • Type.GetProperties 返回的属性将其 ReflectedType 设置为 BaseDerived,具体取决于关于实际对象的类型。

不知道怎么解决。我可以检查 lambda 中参数的类型,但在下一步中我想允许像 Ignore(x=>x.Some.Deep.Property) 这样的构造,所以它可能不会这样做。

如有任何关于如何比较 PropertyInfo 或如何从 lambda 正确检索它们的建议,我们将不胜感激。

最佳答案

FindProperty 告诉您反射的 TypeBase 的原因是因为这是 lambda 将用于调用的类。

你可能知道这个:)

您可以使用这个代替 Type 的 GetProperties()

static IEnumerable<PropertyInfo> GetMappedProperties(Type type)
{
return type
.GetProperties()
.Select(p => GetMappedProperty(type, p.Name))
.Where(p => p != null);
}

static PropertyInfo GetMappedProperty(Type type, string name)
{
if (type == null)
return null;

var prop = type.GetProperty(name);

if (prop.DeclaringType == type)
return prop;
else
return GetMappedProperty(type.BaseType, name);
}

要解释更多关于为什么 lambda 实际上直接使用 Base 方法,并且您看到本质上不同的 PropertyInfo,可能更好地解释看 IL

考虑这段代码:

static void Foo()
{
var b = new Base { Id = 4 };
var d = new Derived { Id = 5 };

decimal dm = b.Id;
dm = d.Id;
}

这里是 b.Id 的 IL

IL_002f: callvirt instance valuetype [mscorlib]System.Decimal ConsoleApplication1.Base::get_Id()

还有 d.Id 的 IL

IL_0036: callvirt instance valuetype [mscorlib]System.Decimal ConsoleApplication1.Base::get_Id()

关于c# - 比较来自 Type.GetProperties() 和 lambda 表达式的 PropertyInfo,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10113584/

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