如何取消使用ref参数调用方法的ref关键字限制?

时间:2019-02-21 05:24:02

标签: c# .net c#-7.2

请注意,这个问题不是要打破语言对发展的限制,而只是为了研究可能性。

C#的语言设计师希望调用者知道他们在做什么,因此,通常您不能简单地在调用时删除ref(或out)关键字,例如foo(ref bar)foo(bar)。 但是,无论设计理念如何,如果您真的想打破语言限制怎么办?

目的:

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})");

输出

a=(8,7),b=(6,5)

1 个答案:

答案 0 :(得分:0)

在没有任何其他关键字的情况下通过引用传递参数的唯一解决方案是C#7.2中的The in modifier on parameters。它在调用时不需要显式的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)