CommunityToolkit / CommunityToolkit/dotnet
RefTuple
- Dominant language
- C#
- Stars
- 3.8k
- Forks
- 400
- PR merge metrics
- No merged PRs in 30d
Description
### Overview
Adding `RefTuple<...>` types would allow ref structs to be used in tuples.
### API breakdown
```cs
public ref struct RefTuple
where T1 : allows ref struct
where T2 : allows ref struct
{
public RefTuple(T1 t1, T2 t2);
public T1 Item1 { get; set; }
public T2 Item2 { get; set; }
public readonly void Deconstruct(out T1 item1, out T2 item2);
}
```
### Usage example
This is an example using pattern matching to select a reference to a field, and a paired value. The `ref` is wrapped in a `Ref`, which as a `ref struct` cannot be stored in a normal tuple or `ValueTuple`.
```cs
// Get reference to selected register argument
RefTuple, RegisterSet> pair = target switch
{
// General Purpose Registers
Argument.RS => new(new(ref _rs), RegisterSet.GeneralPurpose),
Argument.RT => new(new(ref _rt), RegisterSet.GeneralPurpose),
Argument.RD => new(new(ref _rd), RegisterSet.GeneralPurpose),
// Float Registers
Argument.FS => new(new(ref _rs), RegisterSet.FloatingPoints),
Argument.FT => new(new(ref _rt), RegisterSet.FloatingPoints),
Argument.FD => new(new(ref _rd), RegisterSet.FloatingPoints),
// RT Register for coprocessors
Argument.RT_Numbered => new(new(ref _rt), RegisterSet.Numbered),
// Invalid target type
_ => throw new ArgumentOutOfRangeException($"Argument of type '{target}' attempted to parse as a register.")
};
(Ref regRef, RegisterSet set) = pair;
ref GPRegister reg = ref regRef.Value;
```
### Breaking change?
No
### Alternatives
For the code above, here's what it looks like using a switch statement instead of a switch expression.
```cs
// Get reference to selected register argument
ref GPRegister reg = ref _rs;
RegisterSet set = RegisterSet.GeneralPurpose;
switch (target)
{
// General Purpose Registers
case Argument.RS:
reg = ref _rs;
break;
case Argument.RT:
reg = ref _rt;
break;
case Argument.RD:
reg = ref _rd;
break;
// Float Registers
case Argument.FS:
reg = ref _rs;
set = RegisterSet.FloatingPoints;
break;
case Argument.FT:
reg = ref _rt;
set = RegisterSet.FloatingPoints;
break;
case Argument.FD:
reg = ref _rd;
set = RegisterSet.FloatingPoints;
break;
// RT Register for coprocessors
case Argument.RT_Numbered:
reg = ref _rt;
set = RegisterSet.Numbered;
break;
// Invalid target type
default:
return ThrowHelper.ThrowArgumentOutOfRangeException($"Argument of type '{target}' attempted to parse as a register.");
}
```
Perfectly legitimate code, but the pattern matching is much more convenient to read.
### Additional context
This requires .NET 9 and C# version 13
### Help us help you
Yes, I'd like to be assigned to work on this item
Contributor guide
Assessment
This issue has not been assessed yet.