Reputation: 2642
I am creating a matrix struct and am trying to add an indexer to the matrix like so:
public struct Vector4f
{
public float X;
public float Y;
public float Z;
public float W;
}
public struct Matrix4x4f
{
public Vector4f X;
public Vector4f Y;
public Vector4f Z;
public Vector4f W;
public ref Vector4f this[int index]
{
get
{
return ref Unsafe.Add(ref X, index);
}
}
}
I am unable to get rid of the error in the getter however.
CS8347: Cannot use a result of 'Unsafe.Add(ref Vector4f, int)' in this context because it may expose variables referenced by parameter 'source' outside of their declaration scope.
I there a way to do what I am trying to do? The goal is to be able to write the following (while still using value types):
var m = new Matrix4x4f();
m[2].X = 3.14f;
Upvotes: 1
Views: 489
Reputation: 601
You can achieve what you need by annotating the indexer (or its getter) with [System.Diagnostics.CodeAnalysis.UnscopedRef]
:
[System.Diagnostics.CodeAnalysis.UnscopedRef]
public ref Vector4f this[int index]
{
get
{
return ref Unsafe.Add(ref X, index);
}
}
This way, you don't need to use the unsafe
context.
While normally instance methods of structs are not allowed to return a reference to this
(or any member of this
), the annotation overrides this behavior and makes returning a reference to this
possible.
This part of the documentation on low level struct improvements in C# 11 contains more information on [UnscopedRef]
.
Upvotes: 2
Reputation: 8844
This is probably related to CS8170 which is put in place to prevent references to fields of local / temporary instances to escape the scope in which they're valid. S. Why can't a C# struct method return a reference to a field, but a non-member method can?
As provided by an answer there, you can still go "full unsafe" by using this, but then it's your responsibility to not wreak havoc with references to instances that went out of scope.
public unsafe ref Vector4f this[int index]
{
get
{
fixed (Vector4f* pX = &X)
return ref *(pX + index);
}
}
Upvotes: 1
Reputation: 2371
You can add another indexer property with two parameters for access to vector components:
public double this[int index1, int index2]
{
get
{
switch (index1)
{
case 0: return X[index2];
case 1: return Y[index2];
case 2: return Z[index2];
case 3: return W[index2];
}
throw new ArgumentOutOfRangeException(nameof(index1));
}
set
{
switch (index1)
{
case 0: X[index2] = value; break;
case 1: Y[index2] = value; break;
case 2: Z[index2] = value; break;
case 3: W[index2] = value; break;
}
throw new ArgumentOutOfRangeException(nameof(index1));
}
}
}
Upvotes: 0