테스트 프레임 워크를 만드는 동안 이상한 문제가 발견되었습니다.Type.GetProperties() 및 람다 식에서 PropertyInfo를 비교하십시오.
동일한 유형의 객체를 해당 속성으로 비교할 수 있지만 일부는 무시할 수있는 정적 클래스를 만들고 싶습니다.
나는 이것에 대한 간단한 유창하게 API를 갖고 싶어, 그래서 주어진 객체가 Id
및 Name
을 제외한 모든 특성 (가 어떤지를 검사하지 않습니다)에 동일한 경우 TestEqualityComparer.Equals(first.Ignore(x=>x.Id).Ignore(y=>y.Name), second);
같은 호출은 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
에 의해 반환특성은 실제 개체의 유형에 따라
Base
또는Derived
자신의ReflectedType
세트가있다.
나는 그것을 해결하는 방법을 모른다. 람다에서 매개 변수의 유형을 확인할 수는 있지만 다음 단계에서는 Ignore(x=>x.Some.Deep.Property)
과 같은 구조를 허용하므로 아마도 그렇게하지 않을 것입니다.
PropertyInfo
을 비교하는 방법이나 lambda에서 적절히 검색하는 방법에 대한 제안은 감사하겠습니다.
GetProperties에서 BindingFlags.FlattenHierarchy 값으로 재생 해 보았습니까? 그것이 뭐가 바뀌는 지 보시오. –
거기에는 행운이 없지만 제안 해 주셔서 감사합니다. 나는 ** BindingFlags가 반환되는 멤버 만 변경할 수 있지만 자신의 속성에는 영향을 미치지 않을 것이라고 생각합니다. 나는이 솔루션이 FindProperty와 관련되어있을 것이라고 믿는다. –
구성원 이름으로 GetProperty (표현식을 통해 얻을 수도 있음) 유형을 얻은 후에 FindProperty에 두 번째 해킹 된 단계를 추가 할 수 있습니까? 해킹이지만 작동 할 수도 있습니다. –