두 개의 데이터 전송 객체에 C#에 동일한 속성이 있는지 신속하게 확인하는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/986572

  •  13-09-2019
  •  | 
  •  

문제

이 데이터 전송 개체가 있습니다.

public class Report 
{
    public int Id { get; set; }
    public int ProjectId { get; set; }
    //and so on for many, many properties.
}

글을 쓰고 싶지 않습니다

public bool areEqual(Report a, Report b)
{
    if (a.Id != b.Id) return false;
    if (a.ProjectId != b.ProjectId) return false;
    //Repeat ad nauseum
    return true;
}

속성 만있는 두 객체 만 동일한 값을 가지고 있는지 테스트하는 더 빠른 방법이 있습니까 (한 줄의 코드 또는 속성 당 하나의 논리 표현식이 필요하지 않은 것은 무엇입니까?)

structs로 전환하는 것은 옵션이 아닙니다.

도움이 되었습니까?

해결책

아마도 약간의 반사는 어떻습니까? Expression.Compile() 공연을 위해? (여기서 정적 CTOR는 우리가 당면 한 번만 컴파일 할 수 있도록하십시오. T):

using System;
using System.Linq.Expressions;

public class Report {
    public int Id { get; set; }
    public int ProjectId { get; set; }
    static void Main() {
        Report a = new Report { Id = 1, ProjectId = 13 },
            b = new Report { Id = 1, ProjectId = 13 },
            c = new Report { Id = 1, ProjectId = 12 };
        Console.WriteLine(PropertyCompare.Equal(a, b));
        Console.WriteLine(PropertyCompare.Equal(a, c));
    }
}
static class PropertyCompare {
    public static bool Equal<T>(T x, T y) {
        return Cache<T>.Compare(x, y);
    }
    static class Cache<T> {
        internal static readonly Func<T, T, bool> Compare;
        static Cache() {
            var props = typeof(T).GetProperties();
            if (props.Length == 0) {
                Compare = delegate { return true; };
                return;
            }
            var x = Expression.Parameter(typeof(T), "x");
            var y = Expression.Parameter(typeof(T), "y");

            Expression body = null;
            for (int i = 0; i < props.Length; i++) {
                var propEqual = Expression.Equal(
                    Expression.Property(x, props[i]),
                    Expression.Property(y, props[i]));
                if (body == null) {
                    body = propEqual;
                } else {
                    body = Expression.AndAlso(body, propEqual);
                }
            }
            Compare = Expression.Lambda<Func<T, T, bool>>(body, x, y)
                          .Compile();
        }
    }
}

편집 : 필드를 처리하도록 업데이트 :

static class MemberCompare
{
    public static bool Equal<T>(T x, T y)
    {
        return Cache<T>.Compare(x, y);
    }
    static class Cache<T>
    {
        internal static readonly Func<T, T, bool> Compare;
        static Cache()
        {
            var members = typeof(T).GetProperties(
                BindingFlags.Instance | BindingFlags.Public)
                .Cast<MemberInfo>().Concat(typeof(T).GetFields(
                BindingFlags.Instance | BindingFlags.Public)
                .Cast<MemberInfo>());
            var x = Expression.Parameter(typeof(T), "x");
            var y = Expression.Parameter(typeof(T), "y");

            Expression body = null;
            foreach(var member in members)
            {
                Expression memberEqual;
                switch (member.MemberType)
                {
                    case MemberTypes.Field:
                        memberEqual = Expression.Equal(
                            Expression.Field(x, (FieldInfo)member),
                            Expression.Field(y, (FieldInfo)member));
                        break;
                    case MemberTypes.Property:
                        memberEqual = Expression.Equal(
                            Expression.Property(x, (PropertyInfo)member),
                            Expression.Property(y, (PropertyInfo)member));
                        break;
                    default:
                        throw new NotSupportedException(
                            member.MemberType.ToString());
                }
                if (body == null)
                {
                    body = memberEqual;
                }
                else
                {
                    body = Expression.AndAlso(body, memberEqual);
                }
            }
            if (body == null)
            {
                Compare = delegate { return true; };
            }
            else
            {
                Compare = Expression.Lambda<Func<T, T, bool>>(body, x, y)
                              .Compile();
            }
        }
    }
}

다른 팁

원래 (질문 1831747)

내 확인 memberwiseequality comparer 그것이 당신의 요구에 맞는지 확인합니다.

사용하기 쉽고 매우 효율적입니다. IL-EMIT를 사용하여 첫 번째 실행에서 전체 평형 및 gethashcode 함수를 생성합니다 (사용 된 각 유형에 대해 한 번). 해당 유형의 기본 평등 비교 (EqualityComparer.Default)를 사용하여 주어진 개체의 각 필드 (개인 또는 공개)를 비교합니다. 우리는 한동안 생산에서 그것을 사용해 왔으며 안정적으로 보이지만 보증을 남기지 않을 것입니다 =)

그것은 당신이 자신의 평등 방법을 굴릴 때 거의 생각하지 않는 모든 pescy edge cases를 처리해야합니다 (즉, 먼저 상자에 박스를 뽑고 로트를 끄지 않는 한 자신의 객체를 null과 비교할 수 없습니다. 더 많은 널 관련 문제).

나는 그것에 대한 블로그 게시물을 작성하는 것을 의미했지만 아직 그것에 대해서는 얻지 못했습니다. 코드는 약간 문서화되지 않았지만 마음에 들면 약간 청소할 수 있습니다.

public override int GetHashCode()
{
    return MemberwiseEqualityComparer<Foo>.Default.GetHashCode(this);
}

public override bool Equals(object obj)
{
    if (obj == null)
        return false;

    return Equals(obj as Foo);
}

public override bool Equals(Foo other)
{
    return MemberwiseEqualityComparer<Foo>.Default.Equals(this, other);
}

Memberwiseequality Comparer는 MIT 라이센스 Meaining은 라이센스를 약간 변경하지 않고 독점 솔루션에서 사용하는 것을 포함하여 원하는대로 거의 할 수 있습니다.

나는 MARC의 코드를 내 자신의 용도로 본격적인 iqueality comparer 구현으로 확장했으며, 이것이 향후 다른 사람들에게 유용 할 수 있다고 생각했습니다.

/// <summary>
/// An <see cref="IEqualityComparer{T}"/> that compares the values of each public property.
/// </summary>
/// <typeparam name="T"> The type to compare. </typeparam>
public class PropertyEqualityComparer<T> : IEqualityComparer<T>
{
    // http://stackoverflow.com/questions/986572/hows-to-quick-check-if-data-transfer-two-objects-have-equal-properties-in-c/986617#986617

    static class EqualityCache
    {
        internal static readonly Func<T, T, bool> Compare;
        static EqualityCache()
        {
            var props = typeof(T).GetProperties();
            if (props.Length == 0)
            {
                Compare = delegate { return true; };
                return;
            }
            var x = Expression.Parameter(typeof(T), "x");
            var y = Expression.Parameter(typeof(T), "y");

            Expression body = null;
            for (int i = 0; i < props.Length; i++)
            {
                var propEqual = Expression.Equal(
                    Expression.Property(x, props[i]),
                    Expression.Property(y, props[i]));
                if (body == null)
                {
                    body = propEqual;
                }
                else
                {
                    body = Expression.AndAlso(body, propEqual);
                }
            }
            Compare = Expression.Lambda<Func<T, T, bool>>(body, x, y).Compile();
        }
    }

    /// <inheritdoc/>
    public bool Equals(T x, T y)
    {
        return EqualityCache.Compare(x, y);
    }

    static class HashCodeCache
    {
        internal static readonly Func<T, int> Hasher;
        static HashCodeCache()
        {
            var props = typeof(T).GetProperties();
            if (props.Length == 0)
            {
                Hasher = delegate { return 0; };
                return;
            }
            var x = Expression.Parameter(typeof(T), "x");

            Expression body = null;
            for (int i = 0; i < props.Length; i++)
            {
                var prop = Expression.Property(x, props[i]);
                var type = props[i].PropertyType;
                var isNull = type.IsValueType ? (Expression)Expression.Constant(false, typeof(bool)) : Expression.Equal(prop, Expression.Constant(null, type));
                var hashCodeFunc = type.GetMethod("GetHashCode", BindingFlags.Instance | BindingFlags.Public);
                var getHashCode = Expression.Call(prop, hashCodeFunc);
                var hashCode = Expression.Condition(isNull, Expression.Constant(0, typeof(int)), getHashCode);

                if (body == null)
                {
                    body = hashCode;
                }
                else
                {
                    body = Expression.ExclusiveOr(Expression.Multiply(body, Expression.Constant(typeof(T).AssemblyQualifiedName.GetHashCode(), typeof(int))), hashCode);
                }
            }
            Hasher = Expression.Lambda<Func<T, int>>(body, x).Compile();
        }
    }

    /// <inheritdoc/>
    public int GetHashCode(T obj)
    {
        return HashCodeCache.Hasher(obj);
    }
}

불행히도 필드 값을 비교하기 위해 방법을 작성해야합니다. System.ValueType 반사를 사용하고 A의 필드 값을 비교하도록 만들어졌습니다. struct 그러나 성능이 느리기 때문에 이것조차도 볼 수 없습니다. 가장 좋은 방법은 Equals 방법과도 구현 IEquatable<T> 강력하게 입력 한 인터페이스 Equals 초과 적재.

당신이 그것에있는 동안, 당신은 좋은 것을 제공 할 수도 있습니다 GetHashCode 보완하기 위해 재정의 Equals 구현. 이 모든 단계는 좋은 관행으로 간주됩니다.

이를 위해 반사를 사용해야합니다.이 링크를 따르십시오 -> C#의 객체 속성 비교

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top