Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7791447
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 1, 20262026-06-01T21:53:23+00:00 2026-06-01T21:53:23+00:00

While creating my testing framework I’ve found a strange problem. I want to create

  • 0

While creating my testing framework I’ve found a strange problem.

I want to create a static class that would allow me to compare objects of the same type by their properties, but with possibility to ignore some of them.

I want to have a simple fluent API for this, so a call like TestEqualityComparer.Equals(first.Ignore(x=>x.Id).Ignore(y=>y.Name), second); will return true if the given objects are equal on every property except Id and Name (they will not be checked for equality).

Here goes my code. Of course it’s a trivial example (with some obvious overloads of methods missing), but I wanted to extract the simplest code possible. The real case scenario’s a bit more complex, so I don’t really want to change the approach.

The method FindProperty is almost a copy-paste from AutoMapper library.

Object wrapper for fluent API:

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

Fluent stuff:

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

The actual comparer:

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

That’s basically it.

And here are two test snippets, the first one works, the second one fails:

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

The problem is with the Except method in DoMyEquals.

Properties returned by FindProperty are not equal to those returned by Type.GetProperties. The difference I spot is in PropertyInfo.ReflectedType.

  • regardless to the type of my objects, FindProperty tells me that the reflected type is Base.

  • properties returned by Type.GetProperties have their ReflectedType set to Base or Derived, depending on the type of actual objects.

I don’t know how to solve it. I could check the type of the parameter in lambda, but in the next step I want to allow constructs like Ignore(x=>x.Some.Deep.Property), so it probably will not do.

Any suggestion on how to compare PropertyInfo‘s or how to retrieve them from lambdas properly would be appreciated.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-01T21:53:24+00:00Added an answer on June 1, 2026 at 9:53 pm

    The reason FindProperty is telling you the reflected Type is Base is because that’s the class the lambda would use for the invocation.

    You probably know this 🙂

    Instead of GetProperties() from Type, could you use this

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

    To explain more about why the lambda is actually using the Base method directly, and you see essentially a different PropertyInfo, might be better explained looking at the IL

    Consider this code:

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

    And here is the IL for b.Id

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

    And the IL for d.Id

    IL_0036: callvirt instance valuetype [mscorlib]System.Decimal ConsoleApplication1.Base::get_Id()
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I am facing a problem while creating a static cursor in DB2. This is
While creating JavaScript with ASP.NET MVC I noticed several scope warnings and realized that
While testing out a UDP multicast server that I've written on Windows 7 Ultimate
I am creating a simple project that will allow me to upload and download
I im creating a simple testing app that runs a check every hour on
I am creating a class that determines which of number of registered WCF client
recently i came across strange problem while uploading files to my new server. I
I'm currently stacked with the web service that im creating right now. when Testing
While creating the setup for VB.net application I am getting the following warning: Warning
while creating a thread in java, there is two ways such as Extending threads

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.