2

カスタム IComparer 実装の奇妙な動作に出くわし、それを修正して期待される動作を得る方法を理解できないようです。

指定した Int32 値で Guid の最初の 4 バイトを上書きできる System.Guid の拡張メソッドを提供する静的クラスを作成しました。これは、人口の多いデータベース テーブルのインデックスに適したセミシーケンシャル Guid を作成できるようにする目的で行われます。

public static class GuidExt
{
    public static Guid Sequence(this Guid obj, int index)
    {
        byte[] b = obj.ToByteArray();
        BitConverter.GetBytes(index).CopyTo(b, 0);
        return new Guid(b);
    }
}

非常に簡単で、期待どおりに機能します。

私が作成したカスタム Comparer クラスは、Guid の挿入された Int32 部分の昇順で Guid を並べ替えることができるように設計されています。実装は次のとおりです。

public class GuidSequenceComparer : IComparer<Guid>
{
    public int Compare(Guid x, Guid y)
    {
        var xBytes = x.ToByteArray();
        var yBytes = y.ToByteArray();
        byte[] xIndexBytes = new byte[4];
        for (int i = 0; i < 4; i++)
        {
            xIndexBytes[i] = xBytes[0];
        }
        byte[] yIndexBytes = new byte[4];
        for (int i = 0; i < 4; i++)
        {
            yIndexBytes[i] = yBytes[i];
        }
        var xIndex = BitConverter.ToInt32(xIndexBytes, 0);
        var yIndex = BitConverter.ToInt32(yIndexBytes, 0);

        return xIndex.CompareTo(yIndex);

        //// The following was used to test if any sort was being performed
        //// and reverses the ordering (see below paragraph)
        // if (xIndex > yIndex)
        // {
        //     return -1;
        // }
        // if (xIndex < yIndex)
        // {
        //     return 1
        // }
        // return 0;
    }
}

List でこのカスタム比較子を使用すると、並べ替えは実​​行されますが、List オブジェクト内のインデックス位置が並べ替えられます。なぜこれが起こっているのかわかりませんが、コメントアウトされたセクションに示されているように比較の結果を逆にすることで確認しました.Guid内のint値ではなく、既存の位置に基づいてリストの順序を逆にしただけです.リスト内。なぜこれが起こっているのか、私は完全に迷っています。

動作を試して再現したい場合は、コンソール アプリ内で使用した簡単なテストを次に示します。

        List<Guid> guidList = new List<Guid>();

        guidList.Add(Guid.NewGuid().Sequence(5));
        guidList.Add(Guid.NewGuid().Sequence(3));
        guidList.Add(Guid.NewGuid().Sequence(8));
        guidList.Add(Guid.NewGuid().Sequence(1));

        Console.WriteLine("unsorted:");
        foreach (Guid item in guidList)
        {
            Console.WriteLine(item);
        }

        guidList.Sort(new GuidSequenceComparer());

        Console.WriteLine("sorted:");
        foreach (Guid item in guidList)
        {
            Console.WriteLine(item);
        }

        Console.ReadLine();
4

3 に答える 3

3

拡張メソッドを次のように調整することを検討してください。

public static class GuidExt
{
    public static Guid Sequence(this Guid g, int sequenceNum)
    {
        var bytes = g.ToByteArray();

        BitConverter.GetBytes(sequenceNum).CopyTo(bytes, 0);

        return new Guid(bytes);
    }

    public static int GetSequenceNum(this Guid g)
    {
        return BitConverter.ToInt32(g.ToByteArray(), 0);
    }
}

メソッドには、必要のない余分な回転がかなりあります。興味深いことに、これが Jalayn が見つけたエラーの原因でした。

次に、比較子を変更して、より簡単なことを行うことができます。

public int Compare(Guid x, Guid y)
{
    return x.GetSequenceNum().CompareTo(y.GetSequenceNum());
}

お役に立てれば。

于 2013-04-30T13:16:44.513 に答える
3

比較を少し最適化します

public int Compare(Guid x, Guid y)
{
    var xBytes = x.ToByteArray();
    var yBytes = y.ToByteArray();
    int result = 0;
    for (int i = 0; i < 4; i++)
    {
        var result = xBytes[i].CompareTo(yBytes[i]);
        if (result != 0)
        {
            break;
        }
    }

    return result;
}

Buffer.BlockCopyも少し速くArray.Copy

public static Guid Sequence(this Guid source, int index)
{
    var buffer = source.ToByteArray();
    Buffer.BlockCopy(BitConvertor.GetBytes(index), 0, buffer, 0, sizeof(int));
    return new Guid(buffer);
}

またはこれはどうですか、コピーは必要ありません

public static Guid Sequence(this Guid source, int index)
{
    var buffer = source.ToByteArray();
    return new Guid(
        index,
        BitConvertor.ToInt16(buffer, 4),
        BitConvertor.ToInt16(buffer, 6),
        buffer[8],
        buffer[9],
        buffer[10],
        buffer[11],
        buffer[12],
        buffer[13],
        buffer[14],
        buffer[15]);
}
于 2013-04-30T13:14:28.087 に答える
3

あなたの比較では、私はその行だと思います

        xIndexBytes[i] = xBytes[0];

する必要があります

        xIndexBytes[i] = xBytes[i];

それが問題なのかどうかはわかりませんが、これはまさに私に時々起こるようなことです:-)

Array.Copyユーザー@Magnusは私を打ち負かしましたが、次のように、関数を使用してコピーループを回避できます。

Array.Copy(xBytes, 0, xIndexBytes, 0, 4);
于 2013-04-30T13:04:54.563 に答える