Единственным решением для передачи аргумента по ссылке без дополнительного ключевого слова является Модификатор in
для параметров в C # 7.2.Он не требует явного ключевого слова in
при вызове, поскольку он предназначен для вызывающей стороны, чтобы не назначать какие-либо значения аргументу by-ref, чтобы вызывающим абонентам не нужно было уведомляться о том, что они делают.Но на самом деле in
- это не более чем ref
с атрибутом, который помечает его как предназначенный только для чтения, компилятор C # не допускает какого-либо присваивания.Но когда ссылка передается каким-либо иным способом, кроме присваивания (включая строго типизированную передачу), это ограничение может быть преодолено.
public class RefUnlocker
{
private delegate ref T InToRef<T>(in T arg) where T : struct;
private static ref T RefWrapper<T>(ref T arg) where T : struct => ref arg;
private static readonly MethodInfo _refWrapper = typeof(RefUnlocker)
.GetMethod(nameof(RefWrapper), BindingFlags.Static | BindingFlags.NonPublic);
public static ref T Unlock<T>(in T arg) where T : struct
=> ref ((InToRef<T>)Delegate.CreateDelegate(typeof(InToRef<T>),
_refWrapper.MakeGenericMethod(typeof(T))))(arg);
}
Метод RefUnlocker.Unlock
помогает преобразовать параметр in
в ref
один путем динамического генерирования делегата из метода (поскольку параметры in
также являются типами ByRef, которые совпадают с параметрами ref
, поэтому сигнатура метода соответствует делегату), что означает, что вы можете просто написать метод Swap
, например
public static void Swap<T>(ref this T op1, in T op2)
where T : struct
{
var temp = op1;
op1 = op2;
RefUnlocker.Unlock(op2) = temp;
}
(Обратите внимание, что параметр ref в качестве параметра this
в методе расширения не нуждается в ключевом слове ref при вызове, поскольку он не передается как параметр)
Итогда вы можете позвонить без каких-либо дополнительных ключевых слов, таких как
var (a, b) = ((5, 6), (7, 8));
a.Swap(b);
a.Item1.Swap(a.Item2);
b.Item1.Swap(b.Item2);
Console.WriteLine($"a=({a.Item1},{a.Item2}),b=({b.Item1},{b.Item2})");
// Outputs: a=(8,7),b=(6,5)