CommunityToolkit / CommunityToolkit/dotnet

RefTuple

Open
#1,128 1 comment 0 reactions 0 assignees View on GitHub
feature request :mailbox_with_mail:
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.