0

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;
            }
  • What's "`T`"? Did you forget the type parameter? Is it coming from a generic type that contains it? The code shown lacks important context. – madreflection Mar 04 '22 at 23:26
  • Can you provide example code that doesn’t compile in your question? – stuartd Mar 04 '22 at 23:32
  • I wanted to ask this generically because T is a custom type. In the case that led to me asking this question it is a readonly struct containing 104 bytes of floats and ints. – sqldatabunnies Mar 04 '22 at 23:32
  • @stuartd Yep - I just added a more complete example that has all of the important stuff. (actually going to add some more but it'll be up in a minute) – sqldatabunnies Mar 04 '22 at 23:45
  • First thing, I strongly recommend that you don't declare `QuadDataComparer` within `SpriteBatcher`. Especially here, there's just no benefit to doing so. – madreflection Mar 04 '22 at 23:45
  • Second, `IComparer` doesn't support using `ref` parameters (`in` is just `ref` with the guarantee that the receiver won't write anything to that address), so you're probably going to have to write your own sorting implementation that takes a different interface. – madreflection Mar 04 '22 at 23:46
  • For example, define `IRefComparer` with a `int Compare(in T left, in T right)` signature, and possibly constrain `T` to `struct`s. Then implement sorting that takes `IRefComparer` and a comparer that implements `IRefComparer`. – madreflection Mar 04 '22 at 23:49
  • @madreflection I did have a reason to declare the comparer but I only just edited in that broader context now, it's a part of the encapsulation but I understand why you would say that - I was trying to avoid posting 1000 lines of code. My current solution has been to do a custom sorting algorithm and it looks like that may just be the best thing, so I appreciate you saying that. I really like the idea of defining IRefComparer and I'm probably going to do that! – sqldatabunnies Mar 04 '22 at 23:53
  • Well, it wasn't just about declaring it, but rather it was specifically about declaring it *within another class*. That's rarely necessary. Very rarely. Anyway, I'm glad that was helpful. – madreflection Mar 05 '22 at 00:10

0 Answers0