كيفية التحقق بسرعة إذا كان لديك كائنات نقل البيانات لديها خصائص متساوية في 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;
}

هل هناك طريقة أسرع لاختبار ما إذا كان لديك عنصرين مع خصائص فقط له نفس القيم (شيء لا يتطلب سطرا واحدا من التعليمات البرمجية أو تعبير منطقي واحد لكل خاصية؟)

التبديل إلى الهياكل ليس خيارا.

هل كانت مفيدة؟

المحلول

ماذا عن بعض الانعكاس، وربما باستخدام Expression.Compile() للأداء؟ (لاحظ أن المحاسب الثابت هنا يضمن أننا تجميعها مرة واحدة فقط 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.)

تحقق من بلدي memberwisequalitycale. لمعرفة ما إذا كانت تناسب احتياجاتك.

من السهل حقا استخدامها وكفاءة للغاية أيضا. يستخدم IL-EMIT لإنشاء كامل المساواة ووظيفة Gethashcode في التشغيل الأول (مرة واحدة لكل نوع مستخدم). ستقارن كل حقل (خاص أو عام) من الكائن المحدد باستخدام المساواة الافتراضية مقارنة لهذا النوع (equalcomparer.default). لقد تم استخدامه في الإنتاج لفترة من الوقت ويبدو مستقرا ولكني سأترك أي ضمانات =)

يعتني بجميع حالات حافة Pescy التي تظن أنها نادرا ما تفكر فيها عند تدوير طريقة التساوي الخاصة بك (أي، لا يمكنك مقارنة كائنك الخاص مع NULL إلا إذا كنت قد عاشقها في كائن أولا وأول مرة المزيد من القضايا ذات الصلة ب 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);
}

يتم إطلاق سراح العضو ميت الترخيص meaining يمكنك أن تفعل ما تريد معه، بما في ذلك استخدامه في حلول الملكية دون تغيير ترخيصك قليلا.

لقد مددت رمز مارك لتكون تنفيذ Iyequalycomparer بالكامل لاستخداماتي الخاصة، ويعتقد أن هذا قد يكون مفيدا للآخرين في المستقبل:

/// <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 بنيت لاستخدام انعكاس ومقارنة القيم الحقلية ل struct ولكن حتى هذا غير قابل للريح بسبب بطء الأداء. أفضل شيء يجب القيام به هو تجاوز Equals الطريقة وأيضا تنفيذ IEquatable<T> واجهة مكتوبة بشدة Equals الزائد.

أثناء وجودك في ذلك، قد توفر جيدا GetHashCode تجاوز كذلك لاستكمال Equals التنفيذ. كل هذه الخطوات تعتبر ممارسة جيدة.

سوف تحتاج إلى استخدام الانعكاس للقيام بذلك، يرجى اتباع هذا الرابط -> مقارنة خصائص الكائن في C #

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top