如何键入 int 索引?

How can I type an int index?

我在 C# 环境中工作,该环境不允许使用引用类型来引用某些内容。是的,你没看错。我可以使用结构,我可以使用不是对象的特殊数组。

对于 link 数据,我正在使用另一个预先确定的数组的索引。像这样:

static class Data {
    public static float[] Length = new ...;
    public static float[] Weight = new ...;
    // In reality these arrays are NativeArray<T>, provided by the Unity framework
}

int indexLength = 3; // index refers to an entry in the Length array
Data.Length[indexLength] += 1.23;

int indexWeight = 3; // index refers to an entry in the Weight array
Data.Weight[indexWeight] += 1.23;

当然,这与面向对象的方法相反,并且要保持正确对齐更加复杂。

为了帮助完成这项工作,我想修改索引,使它们 'know' 属于哪个数组。我想这类似于 Measure 在 F# 中的作用,尽管我自己从未使用过它。它可能看起来像这样:

WeightIndex indexWeight = (WeightIndex) 3; // The index knows which array it belongs to
Data.Weight[indexWeight] = 42; // OK (maybe implicit cast of indexWeight to int)
Data.Length[indexWeight] = 42; // Compiler error: Wrong index type

有什么方法可以实现这个目标吗?

此外,优化编译器应该完全删除它以避免任何运行时开销。我正在使用 Burst(一种基于 LLVM 的编译器),所以这可能不是问题,除非该方法太复杂以至于无法理解。

最初我在考虑泛型和潜在的运算符覆盖,但后来我想起了扩展方法。

下面是一些使用静态数据 class 和两个静态数组的代码(我显然无法访问 NativeArray 类型),但它会检查传递给数组。

static class DataExtension
{
    public static float Entry(this float[] array, int index)
    {
        return array[index];
    }
    public static void Entry(this float[] array, int index, float value)
    {
        array[index]=value;
    }

    public static int Entry(this int[] array, int index)
    {
        return array[index];
    }
    public static void Entry(this int[] array, int index, int value)
    {
        array[index]=value;
    }
}

及其用途:

static class Data 
{
    public static float[] Length = new float[]{1.0f, 1.1f, 1.2f};
    public static int[] Weight = new int[]{4,5,6};
}
static void Main()
{

    for(var i=0; i< Data.Length.Length; i++)
    {
        Console.WriteLine($"Data.Length[{i}]={Data.Length[i]}");
        
        Console.WriteLine($"Data.Length.Entry(i) = {Data.Length.Entry(i)}");
        
        Data.Length.Entry(i, Data.Length.Entry(i)+0.5f);
        Console.WriteLine($"Data.Length.Entry(i)+0.5f = {Data.Length.Entry(i)}");
        
    }

    for(var i=0; i< Data.Weight.Length; i++)
    {
        Console.WriteLine($"Data.Weight[{i}]={Data.Weight[i]}");
        
        Console.WriteLine($"Data.Weight.Entry(i) = {Data.Weight.Entry(i)}");
        
        Data.Weight.Entry(i, Data.Weight.Entry(i)+2);
        Console.WriteLine($"Data.Weight.Entry(i)+2 = {Data.Weight.Entry(i)}");
        
    }

}

这给出了以下输出:

Data.Length[0]=1
Data.Length.Entry(i) = 1
Data.Length.Entry(i)+0.5f = 1.5
Data.Length[1]=1.1
Data.Length.Entry(i) = 1.1
Data.Length.Entry(i)+0.5f = 1.6
Data.Length[2]=1.2
Data.Length.Entry(i) = 1.2
Data.Length.Entry(i)+0.5f = 1.7
Data.Weight[0]=4
Data.Weight.Entry(i) = 4
Data.Weight.Entry(i)+2 = 6
Data.Weight[1]=5
Data.Weight.Entry(i) = 5
Data.Weight.Entry(i)+2 = 7
Data.Weight[2]=6
Data.Weight.Entry(i) = 6
Data.Weight.Entry(i)+2 = 8

您可以创建一个 MyTypedArray<T, TIndex> 封装数组(或 NativeArray<T>)的结构,并且只允许通过 TIndex 索引进行访问。

例如,MyTypedArray<float, LengthIndex> 只能通过 LengthIndex 进行索引。 LengthIndex 将是一个具有单个 int IndexValue { get; } 属性 的结构。为确保 MyTypedArray 可以访问 IndexValue 属性,TIndex 需要限制为实现定义 属性 的 IIndex 接口的结构。 MyTypedArray 的索引器然后采用 TIndex 而不是 int.

这是一个示例实现 (fiddle):

interface IIndex
{
    int IndexValue { get; }
}

readonly struct MyTypedArray<T, TIndex> where TIndex : struct, IIndex
{
    readonly T[] contents;
    
    public T this[TIndex index]
    {
        get => contents[index.IndexValue];
        set => contents[index.IndexValue] = value;
    }
    
    public MyTypedArray(int length) => contents = new T[length];
}

readonly struct LengthIndex : IIndex
{
    public int IndexValue { get; }
    public LengthIndex(int i) => IndexValue = i;
    public static implicit operator LengthIndex(int i) => new LengthIndex(i);
}

readonly struct WidthIndex : IIndex
{
    public int IndexValue { get; }
    public WidthIndex(int i) => IndexValue = i;
    public static implicit operator WidthIndex(int i) => new WidthIndex(i);
}

public class Program
{
    public static void Main()
    {
        var lengths = new MyTypedArray<float, LengthIndex>(4);
        var widths = new MyTypedArray<float, WidthIndex>(4);
        
        LengthIndex indexLength = 3;    // implicit conversion

        lengths[indexLength] += 1.0f;
        // widths[indexLength] += 1.0f; // won't compile: cannot convert from 'LengthIndex' to 'WidthIndex'

    }
}