10

以下のコードのように 2 つのリストを減算しようとすると、assignUsers3 つのレコードがあり、assignedUsers2 つの行があります。メソッドの後Except、私はまだ3行を取得しますが、2行assignedUsersが似ているため、1レコードを取得する必要がありますassignUsers

 var users = accountApp.GetUsersByAccountId(context.GetUserData().AccountId);
 List<AssignUserViewModel> assignUsers = Mapper.Map<List<AssignUserViewModel>>(users).ToList();
 var mailUsers = mailApp.GetMailAssignedByMailId(id).Select(m => new { m.UserId, m.User.Name }).ToList();
 List<AssignUserViewModel> assignedUsers = mailUsers.Select(Mapper.DynamicMap<AssignUserViewModel>).ToList();
 assignUsers = assignUsers.Except(assignedUsers).ToList();
4

2 に答える 2

29

メソッドを期待どおりに機能させるExceptには、クラスにメソッドが正しくオーバーライドされAssignUserViewModelている必要がGetHashCodeあります。Equals

たとえば、AssignUserViewModelオブジェクトがオブジェクトによって一意に定義されているId場合は、次のようにクラスを定義する必要があります。

class AssignUserViewModel
{
    // other methods...


    public override int GetHashCode()
    {
        return this.Id.GetHashCode();
    }
    public override bool Equals(object obj)
    {
        if (!(obj is AssignUserViewModel))
            throw new ArgumentException("obj is not an AssignUserViewModel");
        var usr = obj as AssignUserViewModel;
        if (usr == null)
            return false;
        return this.Id.Equals(usr.Id);
    }
}

それ以外の場合、クラスの実装を変更できない/変更したくない場合は、を実装IEqualityComparer<>してメソッドに渡すことができますExcept。例:

class AssignUserViewModelEqualityComparer : IEqualityComparer<AssignUserViewModel>
{
    public bool Equals(AssignUserViewModel x, AssignUserViewModel y)
    {
        if (object.ReferenceEquals(x, y))
            return true;
        if(x == null || y == null)
            return false;
        return x.Id.Equals(y.Id);
    }

    public int GetHashCode(AssignUserViewModel obj)
    {
        return obj.Id.GetHashCode();
    }
}

次に、最後の行は次のようになります。

assignUsers = assignUsers.Except(assignedUsers, new AssignUserViewModelEqualityComparer()).ToList();
于 2012-10-20T12:14:03.603 に答える
3

なぜこれが起こるのですか?Set 操作(Distinct、Except、Intersect、Union)を使用する場合、Linq はシーケンス要素が等しいかどうかを比較する必要があります。デフォルトでは、Linq はObject.EqualsおよびObject.GetHashCodeメソッドを使用して要素を比較します。これらのメソッドが型でオーバーライドされていない場合は、参照の等価性によってオブジェクトを比較する基本クラスの実装が使用されます。デフォルトの実装では、同じ参照である 2 つのオブジェクトが同じハッシュ コードを持つことが保証されます (したがって、等しいと見なされます)。これはあなたの場合です。Mapperクラスは、異なる参照を持つオブジェクトの新しいインスタンスを作成しAssignUserViewModel、(すべてのフィールド値が同じであっても) 同等として扱うことはできません。

それで、これで何ができるでしょうか?

  • クラスのオーバーライドEqualsGetHashCodeメソッド。オブジェクトをどのように同等に扱うかはあなた次第です - すべてのフィールド、またはアイデンティティのみ。Linq はメソッドを使用して要素を比較します。

  • Equals独自の比較子を提供します (これは通常、オブジェクトを変更してとをオーバーライドできない場合ですGetHashCode。はい、すべての Linq セット操作には 2 つのオーバーロードがあります。1 つは既定の比較子を使用し、もう 1 つは yours を受け入れますIEqualityComparer<T>

  • 匿名型を使用します。すべての匿名型には、すべてのプロパティの比較を使用してオブジェクトが等しいかどうかを判断するメソッドEqualsおよびが既に生成されています。GetHashCodeこの場合、型を変更したり比較子を作成したりする必要はありません。

したがって、最初の 2 つのアプローチのサンプルは既にあります。最後のアプローチは次のとおりです。

var assignUsers = accountApp.GetUsersByAccountId(context.GetUserData().AccountId)
                            .Select(u => new { u.UserId, u.Name });

var assignedUsers = mailApp.GetMailAssignedByMailId(id)
                           .Select(m => new { m.UserId, m.User.Name });

var assignUsers = assignUser.Except(assignedUsers);
// do not map until here
List<AssignUserViewModel> result = 
             assignUsers.Select(Mapper.DynamicMap<AssignUserViewModel>).ToList();
于 2012-10-20T12:55:10.027 に答える