I'm working in a C# environment that does not allow use of reference types to reference something. Yes, you're reading that right. I can use structs, and I can use special arrays that are not objects.
To link data, I'm using an index into another, predetermined array. Something like this:
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;
This is, of course, opposed to an object oriented approach, and is a more complicated to keep properly aligned.
To help this effort, I would like to amend indexes such that they 'know' which array they belong to. I guess this is similar to what a Measure does in F#, though I have never used that myself yet. It could maybe look like this:
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
Is there any way to achieve this goal?
Additionally, an optimizing compiler should completely remove it to avoid any runtime overhead. I'm using Burst (an LLVM-based compiler), so this is probably not a problem unless the approach is too complicated for it to understand.
CodePudding user response:
Initially I was thinking about Generics and potential operator overriding, but then I remembered extension methods.
Here's some code using a static data class with two static arrays (I obviously don't have access to a NativeArray
type), but it will type check on the value passed to the array.
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;
}
}
And its use:
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)}");
}
}
This gives the following output:
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
CodePudding user response:
You can create a MyTypedArray<T, TIndex>
struct that encapsulates an array (or NativeArray<T>
) and only allows access through TIndex
indexes.
For example, a MyTypedArray<float, LengthIndex>
could only be indexed via a LengthIndex
. LengthIndex
would be a struct with a single int IndexValue { get; }
property. To ensure that MyTypedArray
can access the IndexValue
property, TIndex
needs be constrained to structs implementing the IIndex
interface defining that property. The indexer of MyTypedArray
then takes a TIndex
instead of an int
.
Here's an example implementation (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'
}
}