LINQ GroupBy في حقول متعددة من نوع المرجع؛مقارن المساواة المخصص

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

سؤال

لقد بحثت في حوالي 20 مثالًا على ذلك في SO وفي أماكن أخرى، لكن لم أجد واحدًا يغطي ما أحاول القيام به.هذا - هل يمكنني تحديد مقارن النوع الصريح الخاص بي في السطر؟ - يبدو أنه ما أحتاج إليه، لكنه لا يصل إلى حد كافٍ (أو لا أفهم كيفية المضي قدمًا في الأمر).

  • لدي قائمة LoadData، يحتوي كائن LoadData على حقول لكل من أنواع المرجع والقيمة
  • تحتاج إلى التجميع في مزيج من حقول المرجع والقيمة، وعرض الإخراج على نوع مجهول
  • أحتاج (على ما أظن) إلى توفير IEqualityComparer مخصص لتحديد كيفية مقارنة حقول GroupBy، ولكنها من النوع المجهول

    private class LoadData
    {
        public PeriodEndDto PeriodEnd { get; set; }
        public ComponentDto Component { get; set; }
        public string GroupCode { get; set; }
        public string PortfolioCode { get; set; }
    }
    

أفضل استعلام GroupBy قمت به حتى الآن:

var distinctLoads = list.GroupBy(
    dl => new { PeriodEnd = dl.PeriodEnd, 
                Component = dl.Component, 
                GroupCode = dl.GroupCode },
    (key, data) => new {PeriodEnd = key.PeriodEnd, 
                Component = key.Component, 
                GroupCode = key.GroupCode, 
                PortfolioList = data.Select(d=>d.PortfolioCode)
                                    .Aggregate((g1, g2) => g1 + "," + g2)},
    null);

هذه المجموعات، ولكن لا تزال هناك التكرارات.

  1. كيف يمكنني تحديد رمز مخصص لمقارنة حقول GroupBy؟على سبيل المثال، يمكن مقارنة المكونات بواسطة Component.Code.
هل كانت مفيدة؟

المحلول

المشكلة هنا هي أن نوع مفتاحك مجهول، مما يعني أنه لا يمكنك الإعلان عن فئة يتم تنفيذها IEqualityComparer<T> لهذا النوع من المفاتيح.في حين أنه سيكون ممكن لكتابة مقارن يقارن بين الأنواع المجهولة من أجل المساواة بطريقة مخصصة (عبر طريقة عامة ومندوبين واستدلال نوعي)، لن يكون الأمر ممتعًا للغاية.

ربما يكون الخياران الأبسط هما:

  • اجعل النوع المجهول "يعمل فقط" عن طريق تجاوز Equals/GetHashCode في PeriodEndDto و ComponentDto.إذا كانت هناك مساواة طبيعية تريد استخدامها في كل مكان، فمن المحتمل أن يكون هذا هو الخيار الأكثر عقلانية.أنا أوصي بالتنفيذ IEquatable<T> أيضًا
  • لا تستخدم نوعًا مجهولاً للتجميع - استخدم نوعًا مسمىًا، وبعد ذلك يمكنك التجاوز GetHashCode و Equals على ذلك، أو يمكنك كتابة مقارن مساواة مخصص بالطريقة العادية.

يحرر: ProjectionEqualityComparer لن تنجح حقا.سيكون من الممكن كتابة شيء مماثل بالرغم من ذلك - نوعاً ما CompositeEqualityComparer مما سمح لك بإنشاء مقارنة مساواة من عدة أزواج "إسقاط + مقارنة".سيكون قبيحًا جدًا مقارنة بالنوع المجهول.

نصائح أخرى

يحرر:

كما يشير جون سكيت، يبدو هذا الحل أفضل مما هو عليه الآن، إذا لم تفكر فيه كثيرًا، لأنني نسيت تنفيذ GetHashCode.الاضطرار إلى تنفيذ GethashCode يجعل هذا النهج ، كما يقول جون في إجابته ، "ليست ممتعة للغاية". من المفترض أن هذا هو أيضًا التفسير لغياب (ما يسمى "لا يمكن تفسيره") EqualityComparer<T>.Create() في مجال العمل.سأترك الإجابة كمرجع، كأمثلة لما لا يجب فعله، يمكن أن تكون مفيدة أيضًا.

الإجابة الأصلية:

يمكنك استخدام النهج الذي اقترحه Comparer<T>.Create تم تقديم النمط في .NET 4.5 (ولكنه غائب لسبب غير مفهوم في EqualityComparer<T>).للقيام بذلك، قم بإنشاء DelegateEqualityComparer<T> فصل:

class DelegateEqualityComparer<T> : EqualityComparer<T>
{
    private readonly Func<T, T, bool> _equalityComparison;

    private DelegateEqualityComparer(Func<T, T, bool> equalityComparison)
    {
        if (equalityComparison == null)
            throw new ArgumentNullException("equalityComparison");
        _equalityComparison = equalityComparison;
    }

    public override bool Equals(T x, T y)
    {
        return _equalityComparison(x, y);
    }

    public static DelegateEqualityComparer<T> Create(
        Func<T, T, bool> equalityComparison)
    {
        return new DelegateEqualityComparer<T>(equalityComparison);
    }
}

ثم اكتب أغلفة حول أساليب GroupBy لقبول ملف Func<TKey, TKey, bool> مندوب بدلاً من IEqualityComparer<TKey> معامل.هذه الأساليب تلتف المفوض في ملف DelegateEqualityComparer<T> مثيل، وقم بتمرير ذلك إلى طريقة GroupBy المقابلة.مثال:

public static class EnumerableExt
{
    public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
        this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector,
        Func<TKey, TKey, bool> equalityComparison)
    {
        return source.GroupBy(
            keySelector,
            DelegateEqualityComparer<TKey>.Create(equalityComparison);
    }
}

أخيرًا، في موقع الاتصال الخاص بك، يمكنك استخدام شيء مثل هذا التعبير لـ equalityComparison دعوى:

(a, b) => a.PeriodEnd.Equals(b.PeriodEnd)
    && a.Component.Code.Equals(b.Component.Code)
    && a.GroupCode.Equals(b.GroupCode)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top