Is there a way to use the 'in' keyword with delegates, Func<>s or IComparers?

90 Views Asked by At

I apologize if the question is poorly-worded, this has been a really hard question to phrase or to search for an answer for and it doesn't help that the 'in' keyword is used for contravarient delegates. This is the best way that I can describe it:

The setup:

public delegate int TComparer(in T left, in T right);

// an instance of the delegate, initialized elsewhere
private TComparer _comparer;

// an array of T, initialized elsewhere
private T[] data;

The 'in' keyword means that these operations throw conversion errors:

Array.Sort(data, _comparer); // CS1503

Func<T,T,int> _func = _comparer; // CS1503

Because a function where a parameter uses the 'in' keyword is different from a function that doesn't, it seems to me that there isn't any way to use delegates (or IComparer, etc.) with the keyword in this way. Is there something in the library designed to handle these cases, or is there another way to gain the same efficiency* in a case more specifically like this?

(* Note: In this context T may be a large immutable struct existing in great quantities and that is being accessed so frequently that it would be really useful to avoid defensive copying.)

Edit: Here's a more complete look at the code:

public readonly struct QuadData {

    public readonly VertexPositionColorTexture TL; // 6 floats in a struct
    public readonly VertexPositionColorTexture TR; 
    public readonly VertexPositionColorTexture BL;
    public readonly VertexPositionColorTexture BR;
    public readonly int Depth;
    public readonly int TextureCode;

    // constructors...
}

public class SpriteBatcher {

    public SpriteBatcher() {
        _quadBuffer = Array.Empty<QuadData>();
        _quadComparer = CompareByDepthDescendingThenTexture;

    }

    public delegate int QuadDataComparer(in QuadData left, in QuadData right);

    private QuadDataComparer _quadDataComparer; // initialized in constructor

    private QuadData[] _quadBuffer; // initialized in constructor

    public void Update() {
        Array.Sort(_quadBuffer, _quadDataComparer); // CS1503
    }
}

Some additional stuff that I don't think informs the question itself:

        private void OnSetSortingMode(SpriteSortOption sortOption) {

            if (sortOption == _sortOption) return;
            switch (sortOption) {
                case SpriteSortOption.None:
                    break;
                case SpriteSortOption.Texture:
                    _quadDataComparer = CompareByTexture;
                    break;
                case SpriteSortOption.DepthAscending:
                    _quadDataComparer = CompareByDepthAscendingThenTexture;
                    break;
                case SpriteSortOption.DepthDescending:
                    _quadDataComparer = CompareByDepthDescendingThenTexture;
                    break;
                default:
                    throw new ArgumentException($"Unimplemented SpriteBatcher.SortOptions enumerated value {sortOption} passed to SpriteBatcher.SetSortingMode()");
                }
            if (sortOption != SpriteSortOption.None) {
                _flags |= SpriteBatcherFlags.Sort;
                }
            }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private int CompareByTexture(in QuadData x, in QuadData y) {
            return y.TextureHashCode - x.TextureHashCode; // int.CompareTo(int) is not needed because we don't actually care if a HashCode is larger or smaller, only different.
            }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private int CompareByDepthAscendingThenTexture(in QuadData x, in QuadData y) {
            if (x.Depth < y.Depth) return 1;
            if (x.Depth > y.Depth) return -1;
            return y.TextureHashCode - x.TextureHashCode; // int.CompareTo(int) is not needed because we don't actually care if a HashCode is larger or smaller, only different.
            }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private int CompareByDepthDescendingThenTexture(in QuadData x, in QuadData y) {
            if (x.Depth > y.Depth) return 1;
            if (x.Depth < y.Depth) return -1;
            return y.TextureHashCode - x.TextureHashCode; // int.CompareTo(int) is not needed because we don't actually care if a HashCode is larger or smaller, only different.
            }

        public void SetCustomSortOption(QuadDataComparer comparison) {
            this._quadDataComparer = comparison;
            this._sortOption = SpriteSortOption.Custom;
            }
0

There are 0 best solutions below