Please remove "IN" variant of delegates for Entities.ForEach (e.g VVRI)

I know it makes Entities.ForEach subset of SystemAPI counterparts, but you need to remove all the “I” (readonly) delegates below:

public delegate void VVRI<T0, T1, T2, T3>(T0 t0, T1 t1, ref T2 t2, in T3 t3);
public delegate void VVRR<T0, T1, T2, T3>(T0 t0, T1 t1, ref T2 t2, ref T3 t3);

Because it is basically the same thing (T3& t3 in unsafe, same IL method signature), and the compiler doesn’t recognize that. It only creates confusion.
Sure, It can be defined because it’s delegates; you can define the same delegates over and over, even with the same signature. And I believe this is a mistake.

To prove that, when you test overloads, it gives me the following error:

using System;

public class Program
{
    // Overload #1
    public static void Foo(in int data)
        => Console.WriteLine("Called the 'in' version");
    
    // Overload #2
    public static void Foo(ref int data)
        => Console.WriteLine("Called the 'ref' version");
}
0>Program.cs(10,24): Error CS0663 : 'Program' cannot define an overloaded method that differs only on parameter modifiers 'ref' and 'in'

And Entities.ForEach((int, int, ref a, in b) => …) doesn’t compile because it’s ambiguous (two same ref methods (ref and in))

The call is ambiguous between the following methods or properties: 'LambdaForEachDescriptionConstructionMethods.ForEach<TDescription, T0, T1, T2, T3>(TDescription, VVRI<T0, T1, T2, T3>)' and 'LambdaForEachDescriptionConstructionMethods.ForEach<TDescription, T0, T1, T2, T3>(TDescription, VVRR<T0, T1, T2, T3>)'

Yes, this is an unfortunate design, but it’s not the same as an ambiguous signature. The signature of the ForEach are unique. The issue you have is that you rely on implicit delegate creation which does fail since there are two which match the same signature. As workaround you would have to create the delegate / cast to one of the two delegate types explicitly.

Keep in mind that you can not just pass a method into a callback parameter. You always need an actual delegate instance. In most cases the compiler can implicitly create the delegate instance for you. In this case it can’t.

When your lambda expression does not represent a closure, you could create the delegate instance once and cache it. If you actually use a closure (when you use any external references) you most likely have to recreate the delegate each time or cache it per instance.

1 Like