如何键入 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'
}
}
我在 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'
}
}